diff --git a/.env.example b/.env.example index c2b7a5c..f74dde0 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,56 @@ -APP_ENV=development #development / test / prod -PORT=3069 -DATABASE_URL=postgresql://USERNAME:PASSWORD@HOST:5432/DATABASE?schema=public +# Application +BASE_URL=http://localhost:3069 PORT=3000 NODE_ENV=development -JWT_SECRET=GANTI_DENGAN_SECRET -JWT_REFRESH_SECRET=GANTI_DENGAN_REFRESH_SECRET +# Database (PostgreSQL) +DATABASE_URL=postgresql://user:password@localhost:5432/Database?shema=public -SWAGGER_ENABLED=true +# Authentik (OIDC discovery base) +# Used by OIDC discovery and token exchange (AuthService) +AUTHENTIK_URL=https://auth.raylab.site -LOG_LEVEL=debug \ No newline at end of file +# Authentik Admin API (provisioning identities & groups) +# Example: https://auth.raylab.site/api +AUTHENTIK_API_BASE=https://auth.raylab.site/api + +# Bearer token for Authentik Admin API (admin service account) +AUTHENTIK_API_TOKEN=changeme_admin_api_token + +# Default group name format: RL-{object} +# RayLab will use this group to gate access (default RL-RayLab-Users) +AUTHENTIK_DEFAULT_GROUP=RL-RayLab-Users + +# OIDC / JWKS / Issuer settings for verifying Authentik tokens (if needed) +AUTHENTIK_JWKS_URI=https://auth.raylab.site/.well-known/jwks.json AUTHENTIK_ISSUER=https://auth.raylab.site/application/o/ray-lab-core/ AUTHENTIK_AUDIENCE=ray-lab-core + +# OIDC client credentials (used when RayLab needs to call token endpoint) +AUTHENTIK_CLIENT_ID=raylab-client AUTHENTIK_CLIENT_SECRET=raylab-client-secret + +# RayLab internal JWT (used to sign internal tokens) +RAYLAB_JWT_SECRET=replace_with_a_long_random_secret + +# Token lifetime in seconds +RAYLAB_JWT_EXPIRES_IN=3600 + +# Sync / reconciliation +# Interval in seconds for reconciler to retry PENDING/FAILED syncs +AUTH_RECONCILE_INTERVAL_SECONDS=3600 + +# Auth / sync behavior +# Disable auto-creation of users on login (must remain false per design) +AUTH_AUTO_CREATE_USER=false AUTH_SYNC_EMAIL=true AUTH_SYNC_USERNAME=false + +# Test accounts for integration tests (must exist in Authentik and RayLab DB before tests, +# or be provisioned via RayLab Admin endpoints during test run) +ADMIN_USERNAME=admin@example.com ADMIN_PASSWORD=changeme_admin_password + +OWNER_USERNAME=owner@example.com OWNER_PASSWORD=changeme_owner_password + +EMPLOYEE_USERNAME=employee@example.com EMPLOYEE_PASSWORD=changeme_employee_password + +# Optional: integration test user (overrides defaults used by tests) +TEST_USER_EMAIL=test-integration@example.com TEST_USER_USERNAME=test-integration TEST_USER_PASSWORD=StrongP@ssw0rd! + +# Optional: adjust logging or other runtime flags +LOG_LEVEL=info + +SWAGGER_ENABLED=true \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5cab610 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug NestJS", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "start:debug" + ], + "console": "integratedTerminal", + "restart": true, + "skipFiles": [ + "/**" + ] + } + ] +} \ No newline at end of file diff --git a/Target V1.txt b/Target V1.txt new file mode 100644 index 0000000..602ac97 --- /dev/null +++ b/Target V1.txt @@ -0,0 +1,25 @@ +RayLab Core +│ +├── Identity +│ ├── User +│ ├── Group +│ ├── Role +│ └── Permission +│ +├── Authentication +│ +├── Authorization +│ +├── Audit +│ +├── Scheduler +│ +├── Media +│ +├── Storage +│ +├── Configuration +│ +├── Workflow / Events +│ +└── Registry \ No newline at end of file diff --git a/changes.patch b/changes.patch new file mode 100644 index 0000000..bcd6328 Binary files /dev/null and b/changes.patch differ diff --git a/docs/How To Dev.txt b/docs/How To Dev.txt new file mode 100644 index 0000000..68eccff --- /dev/null +++ b/docs/How To Dev.txt @@ -0,0 +1,46 @@ +Note : +- pastikan berada di branch yang benar. +- + +Requirement : +- Node.js 22 LTS + +Step : +1. jalankan "NPM Install". +2. generate prisma client "npx prisma generate". +3. jalankan "cp env.example env" atau buat file .env baru. +4. sesuaikan isi dari .env + +Run Server : +- npm run start:dev + +Command Dev : +- npm test +- npm run lint +- npm run format +- npx prisma studio {untuk cek db} + +Test API : +- menggunakan postman : + arahkan ke file /postman/RayLab.postman_collection.json +- menggunakan swagger : + - masuk ke http://localhost:port/apilist + - klik authorize dan masukkan jwt token + +Controller Requirement : +- diatas deklarasi class controller pastikan ada ini : + - @ApiTags('object') + - @Controller('object') + - @UseGuards(JwtAuthGuard, PermissionGuard) + +- diatas deklarasi method controller pastikan ada ini : + - tipe api : @Post(), @Get(':id'), @Patch(':id'), @Delete(':id') + - @Permissions(PermissionType.SOMETHING) + - @ApiOperation({ summary: 'penjelasan singkat api' }) + +Table : +- untuk table akan dibuat otomatis oleh schema.prisma, disana hanya perlu ditambahkan model atau diubah, jalankan commandnya, setelah itu otomatis tablenya dibuat +- command : npx prisma migrate dev --name {Nama Perubahan} + +Use case: +- penentuan use case ditentukan siapa pemiliknya dahulu, dan harus konsisten. \ No newline at end of file diff --git a/my-project.bundle b/my-project.bundle new file mode 100644 index 0000000..0d4559c Binary files /dev/null and b/my-project.bundle differ diff --git a/package-lock.json b/package-lock.json index 1056868..53a6213 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,32 +1,95 @@ { "name": "raylab-core", "version": "0.1.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { + "packages": { + "": { + "name": "raylab-core", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@nestjs/common": "^10.4.20", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.4.20", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "^2.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.20", + "@nestjs/swagger": "^7.4.2", + "@prisma/client": "^5.22.0", + "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "dotenv": "^16.6.1", + "ioredis": "^5.3.2", + "jose": "^6.2.6", + "jsonwebtoken": "^9.0.2", + "openid-client": "^6.5.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pino": "^9.7.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2", + "swagger-ui-express": "^5.0.1", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@nestjs/testing": "^10.4.22", + "@playwright/test": "^1.62.1", + "@types/bcrypt": "^5.0.2", + "@types/jest": "^29.5.14", + "@types/node": "^22.13.10", + "@types/passport-jwt": "^4.0.1", + "@types/supertest": "^6.0.3", + "@types/swagger-ui-express": "^4.1.8", + "@typescript-eslint/eslint-plugin": "^8.25.0", + "@typescript-eslint/parser": "^8.25.0", + "eslint": "^9.21.0", + "globals": "^16.0.0", + "jest": "^29.7.0", + "prettier": "^3.5.3", + "prisma": "^5.22.0", + "supertest": "^7.0.0", + "ts-jest": "^29.2.6", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.8.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/compat-data": { + "node_modules/@babel/compat-data": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/core": { + "node_modules/@babel/core": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", @@ -43,289 +106,409 @@ "json5": "^2.2.3", "semver": "^6.3.1" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/generator": { + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-compilation-targets": { + "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, - "requires": { + "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-globals": { + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-module-imports": { + "node_modules/@babel/helper-module-imports": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, - "requires": { + "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-module-transforms": { + "node_modules/@babel/helper-module-transforms": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-plugin-utils": { + "node_modules/@babel/helper-plugin-utils": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-string-parser": { + "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-validator-identifier": { + "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-validator-option": { + "node_modules/@babel/helper-validator-option": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helpers": { + "node_modules/@babel/helpers": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/parser": { + "node_modules/@babel/parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/plugin-syntax-async-generators": { + "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-bigint": { + "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-class-properties": { + "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-class-static-block": { + "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-import-attributes": { + "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-import-meta": { + "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-json-strings": { + "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-jsx": { + "node_modules/@babel/plugin-syntax-jsx": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-logical-assignment-operators": { + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-numeric-separator": { + "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-object-rest-spread": { + "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-catch-binding": { + "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-chaining": { + "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-private-property-in-object": { + "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-top-level-await": { + "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-typescript": { + "node_modules/@babel/plugin-syntax-typescript": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/template": { + "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/traverse": { + "node_modules/@babel/traverse": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", @@ -333,108 +516,144 @@ "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/types": { + "node_modules/@babel/types": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@bcoe/v8-coverage": { + "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@borewit/text-codec": { + "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", - "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==" + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } }, - "@cspotcode/source-map-support": { + "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } + "engines": { + "node": ">=12" } }, - "@eslint-community/eslint-utils": { + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, - "requires": { + "dependencies": { "eslint-visitor-keys": "^3.4.3" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - } + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "@eslint-community/regexpp": { + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } }, - "@eslint/config-array": { + "node_modules/@eslint/config-array": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, - "requires": { + "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/config-helpers": { + "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "requires": { + "dependencies": { "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/core": { + "node_modules/@eslint/core": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "requires": { + "dependencies": { "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/eslintrc": { + "node_modules/@eslint/eslintrc": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, - "requires": { + "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", @@ -445,193 +664,288 @@ "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, - "dependencies": { - "globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true - }, - "ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true - }, - "js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "@eslint/js": { + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { "version": "9.39.5", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } }, - "@eslint/object-schema": { + "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "@eslint/plugin-kit": { + "node_modules/@eslint/plugin-kit": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "requires": { + "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@humanfs/core": { + "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, - "requires": { + "dependencies": { "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "@humanfs/node": { + "node_modules/@humanfs/node": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, - "requires": { + "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "@humanfs/types": { + "node_modules/@humanfs/types": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.18.0" + } }, - "@humanwhocodes/module-importer": { + "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "@humanwhocodes/retry": { + "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "@istanbuljs/load-nyc-config": { + "node_modules/@ioredis/commands": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "@istanbuljs/schema": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "@jest/console": { + "node_modules/@jest/console": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/core": { + "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -660,71 +974,97 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "@jest/environment": { + "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, - "requires": { + "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/expect": { + "node_modules/@jest/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "requires": { + "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/expect-utils": { + "node_modules/@jest/expect-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "requires": { + "dependencies": { "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/fake-timers": { + "node_modules/@jest/fake-timers": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/globals": { + "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/reporters": { + "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, - "requires": { + "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -749,58 +1089,81 @@ "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "@jest/schemas": { + "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "requires": { + "dependencies": { "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/source-map": { + "node_modules/@jest/source-map": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/test-result": { + "node_modules/@jest/test-result": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/test-sequencer": { + "node_modules/@jest/test-sequencer": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "requires": { + "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/transform": { + "node_modules/@jest/transform": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", @@ -816,74 +1179,86 @@ "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/types": { + "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "requires": { + "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jridgewell/gen-mapping": { + "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, - "@jridgewell/remapping": { + "node_modules/@jridgewell/remapping": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, - "@jridgewell/resolve-uri": { + "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.0.0" + } }, - "@jridgewell/sourcemap-codec": { + "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, - "@jridgewell/trace-mapping": { + "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "@lukeed/csprng": { + "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==" + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } }, - "@mapbox/node-pre-gyp": { + "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "requires": { + "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", @@ -893,109 +1268,193 @@ "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, - "@microsoft/tsdoc": { + "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, - "@nestjs/common": { + "node_modules/@nestjs/common": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", - "requires": { + "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", "tslib": "2.8.1", "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "@nestjs/config": { + "node_modules/@nestjs/config": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", - "requires": { + "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", "lodash": "4.17.21" }, - "dependencies": { - "dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" - } + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" } }, - "@nestjs/core": { + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/core": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", - "requires": { + "hasInstallScript": true, + "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "3.3.0", "tslib": "2.8.1", "uid": "2.0.2" - } - }, - "@nestjs/jwt": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", - "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", - "requires": { - "@types/jsonwebtoken": "9.0.5", - "jsonwebtoken": "9.0.2" }, - "dependencies": { - "jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@nestjs/websockets": "^10.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true } } }, - "@nestjs/mapped-types": { + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@nestjs/jwt/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/@nestjs/mapped-types": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.1.tgz", - "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==" + "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0 || ^0.15.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } }, - "@nestjs/passport": { + "node_modules/@nestjs/passport": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", - "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==" + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } }, - "@nestjs/platform-express": { + "node_modules/@nestjs/platform-express": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.22.tgz", "integrity": "sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==", - "requires": { + "dependencies": { "body-parser": "1.20.4", "cors": "2.8.5", "express": "4.22.1", "multer": "2.0.2", "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0" } }, - "@nestjs/swagger": { + "node_modules/@nestjs/swagger": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.4.2.tgz", "integrity": "sha512-Mu6TEn1M/owIvAx2B4DUQObQXqo2028R2s9rSZ/hJEgBK95+doTwS0DjmVA2wTeZTyVtXOoN7CsoM5pONBzvKQ==", - "requires": { + "dependencies": { "@microsoft/tsdoc": "^0.15.0", "@nestjs/mapped-types": "2.0.5", "js-yaml": "4.1.0", @@ -1003,162 +1462,270 @@ "path-to-regexp": "3.3.0", "swagger-ui-dist": "5.17.14" }, - "dependencies": { - "@nestjs/mapped-types": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", - "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==" + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true } } }, - "@noble/hashes": { + "node_modules/@nestjs/swagger/node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/testing": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.22.tgz", + "integrity": "sha512-HO9aPus3bAedAC+jKVAA8jTdaj4fs5M9fing4giHrcYV2txe9CvC1l1WAjwQ9RDhEHdugjY4y+FZA/U/YqPZrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "@nuxtjs/opencollective": { + "node_modules/@nuxtjs/opencollective": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "requires": { + "dependencies": { "chalk": "^4.1.0", "consola": "^2.15.0", "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "@paralleldrive/cuid2": { + "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, - "requires": { + "dependencies": { "@noble/hashes": "^1.1.5" } }, - "@pinojs/redact": { + "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" }, - "@prisma/client": { + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@prisma/client": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", - "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==" + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } }, - "@prisma/debug": { + "node_modules/@prisma/debug": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", - "dev": true + "devOptional": true }, - "@prisma/engines": { + "node_modules/@prisma/engines": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", - "dev": true, - "requires": { + "devOptional": true, + "hasInstallScript": true, + "dependencies": { "@prisma/debug": "5.22.0", "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "@prisma/fetch-engine": "5.22.0", "@prisma/get-platform": "5.22.0" } }, - "@prisma/engines-version": { + "node_modules/@prisma/engines-version": { "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", - "dev": true + "devOptional": true }, - "@prisma/fetch-engine": { + "node_modules/@prisma/fetch-engine": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", - "dev": true, - "requires": { + "devOptional": true, + "dependencies": { "@prisma/debug": "5.22.0", "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "@prisma/get-platform": "5.22.0" } }, - "@prisma/get-platform": { + "node_modules/@prisma/get-platform": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", - "dev": true, - "requires": { + "devOptional": true, + "dependencies": { "@prisma/debug": "5.22.0" } }, - "@sinclair/typebox": { + "node_modules/@sinclair/typebox": { "version": "0.27.12", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", "dev": true }, - "@sinonjs/commons": { + "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "requires": { + "dependencies": { "type-detect": "4.0.8" } }, - "@sinonjs/fake-timers": { + "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^3.0.0" } }, - "@tokenizer/inflate": { + "node_modules/@tokenizer/inflate": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", - "requires": { + "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "@tokenizer/token": { + "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" }, - "@tsconfig/node10": { + "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", "dev": true }, - "@tsconfig/node12": { + "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, - "@tsconfig/node14": { + "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, - "@tsconfig/node16": { + "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "@types/babel__core": { + "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", @@ -1166,329 +1733,325 @@ "@types/babel__traverse": "*" } }, - "@types/babel__generator": { + "node_modules/@types/babel__generator": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@types/babel__template": { + "node_modules/@types/babel__template": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@types/babel__traverse": { + "node_modules/@types/babel__traverse": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.28.2" } }, - "@types/bcrypt": { + "node_modules/@types/bcrypt": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/body-parser": { + "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, - "requires": { + "dependencies": { "@types/connect": "*", "@types/node": "*" } }, - "@types/connect": { + "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/cookiejar": { + "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", "dev": true }, - "@types/estree": { + "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true }, - "@types/express": { + "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, - "requires": { + "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, - "@types/express-serve-static-core": { + "node_modules/@types/express-serve-static-core": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, - "@types/graceful-fs": { + "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/http-errors": { + "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true }, - "@types/istanbul-lib-coverage": { + "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "@types/istanbul-lib-report": { + "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-coverage": "*" } }, - "@types/istanbul-reports": { + "node_modules/@types/istanbul-reports": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-report": "*" } }, - "@types/jest": { + "node_modules/@types/jest": { "version": "29.5.14", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, - "requires": { + "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, - "@types/json-schema": { + "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "@types/jsonwebtoken": { + "node_modules/@types/jsonwebtoken": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", - "requires": { - "@types/node": "*" - }, "dependencies": { - "@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", - "requires": { - "undici-types": "~8.3.0" - } - } + "@types/node": "*" } }, - "@types/methods": { + "node_modules/@types/jsonwebtoken/node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "22.20.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, - "requires": { - "undici-types": "~6.21.0" - }, "dependencies": { - "undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - } + "undici-types": "~6.21.0" } }, - "@types/passport": { + "node_modules/@types/node/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/@types/passport": { "version": "1.0.17", "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*" } }, - "@types/passport-jwt": { + "node_modules/@types/passport-jwt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", "dev": true, - "requires": { + "dependencies": { "@types/jsonwebtoken": "*", "@types/passport-strategy": "*" } }, - "@types/passport-strategy": { + "node_modules/@types/passport-strategy": { "version": "0.2.38", "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*", "@types/passport": "*" } }, - "@types/qs": { + "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true }, - "@types/range-parser": { + "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, - "@types/send": { + "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/serve-static": { + "node_modules/@types/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, - "requires": { + "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, - "@types/stack-utils": { + "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/strip-bom": { + "node_modules/@types/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", "dev": true }, - "@types/strip-json-comments": { + "node_modules/@types/strip-json-comments": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "@types/superagent": { + "node_modules/@types/superagent": { "version": "8.1.11", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz", "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==", "dev": true, - "requires": { + "dependencies": { "@types/cookiejar": "^2.1.5", "@types/methods": "^1.1.4", "@types/node": "*", "form-data": "^4.0.0" } }, - "@types/supertest": { + "node_modules/@types/supertest": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", "dev": true, - "requires": { + "dependencies": { "@types/methods": "^1.1.4", "@types/superagent": "^8.1.0" } }, - "@types/swagger-ui-express": { + "node_modules/@types/swagger-ui-express": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*", "@types/serve-static": "*" } }, - "@types/validator": { + "node_modules/@types/validator": { "version": "13.15.10", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" }, - "@types/yargs": { + "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, - "requires": { + "dependencies": { "@types/yargs-parser": "*" } }, - "@types/yargs-parser": { + "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "@typescript-eslint/eslint-plugin": { + "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/type-utils": "8.65.0", @@ -1497,73 +2060,141 @@ "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/parser": { + "node_modules/@typescript-eslint/parser": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0", "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/project-service": { + "node_modules/@typescript-eslint/project-service": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.65.0", "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/scope-manager": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "@typescript-eslint/visitor-keys": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "@typescript-eslint/tsconfig-utils": { + "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } }, - "@typescript-eslint/type-utils": { + "node_modules/@typescript-eslint/type-utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0", "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/project-service": "8.65.0", "@typescript-eslint/tsconfig-utils": "8.65.0", "@typescript-eslint/types": "8.65.0", @@ -1574,200 +2205,298 @@ "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, - "dependencies": { - "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 - }, - "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, - "requires": { - "balanced-match": "^4.0.2" - } - }, - "minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "requires": { - "brace-expansion": "^5.0.8" - } - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/utils": { + "node_modules/@typescript-eslint/typescript-estree/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, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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, + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "abbrev": { + "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "accepts": { + "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { + "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "acorn": { + "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "acorn-jsx": { + "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "acorn-walk": { + "node_modules/acorn-walk": { "version": "8.3.5", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, - "requires": { + "dependencies": { "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" } }, - "agent-base": { + "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { + "dependencies": { "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "ajv": { + "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-escapes": { + "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { + "dependencies": { "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "append-field": { + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, - "aproba": { + "node_modules/aproba": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==" }, - "are-we-there-yet": { + "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" } }, - "arg": { + "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "array-flatten": { + "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "asap": { + "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "atomic-sleep": { + "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } }, - "babel-jest": { + "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "requires": { + "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", @@ -1775,60 +2504,76 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "babel-plugin-istanbul": { + "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "babel-plugin-jest-hoist": { + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "babel-preset-current-node-syntax": { + "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, - "requires": { + "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1844,49 +2589,74 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, - "babel-preset-jest": { + "node_modules/babel-preset-jest": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, - "requires": { + "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "baseline-browser-mapping": { + "node_modules/baseline-browser-mapping": { "version": "2.11.7", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.7.tgz", "integrity": "sha512-APw5YuIQAg6L9w4sHDI6j26DGFJI6RpYOhnkMPdC9lWbkKvsyPHzDsve1yd73lk21yz7Y09Kci8B2Pp9FonzWA==", - "dev": true + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } }, - "bcrypt": { + "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "requires": { + "hasInstallScript": true, + "dependencies": { "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "body-parser": { + "node_modules/body-parser": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "requires": { + "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", @@ -1900,336 +2670,472 @@ "type-is": "~1.6.18", "unpipe": "~1.0.0" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "brace-expansion": { + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "requires": { + "dependencies": { "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "browserslist": { + "node_modules/browserslist": { "version": "4.28.7", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "bs-logger": { + "node_modules/bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "requires": { + "dependencies": { "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" } }, - "bser": { + "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { + "dependencies": { "node-int64": "^0.4.0" } }, - "buffer-equal-constant-time": { + "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "busboy": { + "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { + "dependencies": { "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" } }, - "bytes": { + "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } }, - "call-bind-apply-helpers": { + "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "call-bound": { + "node_modules/call-bound": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase": { + "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "caniuse-lite": { + "node_modules/caniuse-lite": { "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", - "dev": true + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "char-regex": { + "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "requires": { + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "chownr": { + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } }, - "ci-info": { + "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } }, - "cjs-module-lexer": { + "node_modules/cjs-module-lexer": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, - "class-transformer": { + "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" }, - "class-validator": { + "node_modules/class-validator": { "version": "0.14.4", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", - "requires": { + "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", "validator": "^13.15.22" } }, - "cliui": { + "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "co": { + "node_modules/cluster-key-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } }, - "collect-v8-coverage": { + "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-support": { + "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==" + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "component-emitter": { + "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "concat-stream": { + "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "requires": { + "engines": [ + "node >= 6.0" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "consola": { + "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, - "console-control-strings": { + "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, - "content-disposition": { + "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { + "dependencies": { "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } }, - "convert-source-map": { + "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "cookie": { + "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, - "cookiejar": { + "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, - "cors": { + "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { + "dependencies": { "object-assign": "^4", "vary": "^1" + }, + "engines": { + "node": ">= 0.10" } }, - "create-jest": { + "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", @@ -2237,231 +3143,329 @@ "jest-config": "^29.7.0", "jest-util": "^29.7.0", "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "create-require": { + "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "debug": { + "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "requires": { + "dependencies": { "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "dedent": { + "node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, - "deep-is": { + "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "deepmerge": { + "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "delegates": { + "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "depd": { + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } }, - "destroy": { + "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "detect-libc": { + "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } }, - "detect-newline": { + "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "dezalgo": { + "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "requires": { + "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, - "diff": { + "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "diff-sequences": { + "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "dotenv": { + "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==" + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } }, - "dotenv-expand": { + "node_modules/dotenv-expand": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", - "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==" + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "engines": { + "node": ">=12" + } }, - "dunder-proto": { + "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, - "dynamic-dedupe": { + "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", "dev": true, - "requires": { + "dependencies": { "xtend": "^4.0.0" } }, - "ecdsa-sig-formatter": { + "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" } }, - "ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "electron-to-chromium": { + "node_modules/electron-to-chromium": { "version": "1.5.398", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz", "integrity": "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==", "dev": true }, - "emittery": { + "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "encodeurl": { + "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "es-define-property": { + "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } }, - "es-errors": { + "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } }, - "es-object-atoms": { + "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "requires": { + "dependencies": { "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "es-set-tostringtag": { + "node_modules/es-set-tostringtag": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "requires": { + "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "escalade": { + "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-html": { + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "eslint": { + "node_modules/eslint": { "version": "9.39.5", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", @@ -2497,109 +3501,183 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true - }, - "ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true } } }, - "eslint-scope": { + "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "requires": { + "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-visitor-keys": { + "node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true + "dev": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "espree": { + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "requires": { + "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "esprima": { + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "esquery": { + "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, - "requires": { + "dependencies": { "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "esrecurse": { + "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "requires": { + "dependencies": { "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } }, - "execa": { + "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "requires": { + "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", @@ -2609,32 +3687,44 @@ "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "exit": { + "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "expect": { + "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, - "requires": { + "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "express": { + "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "requires": { + "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", @@ -2667,104 +3757,132 @@ "utils-merge": "1.0.1", "vary": "~1.1.2" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" - } + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "fast-deep-equal": { + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" + }, + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "fast-safe-stringify": { + "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, - "fb-watchman": { + "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "requires": { + "dependencies": { "bser": "2.1.1" } }, - "fdir": { + "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } }, - "fflate": { + "node_modules/fflate": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==" }, - "file-entry-cache": { + "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "requires": { + "dependencies": { "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" } }, - "file-type": { + "node_modules/file-type": { "version": "20.4.1", "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", - "requires": { + "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, - "fill-range": { + "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "finalhandler": { + "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "requires": { + "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -2773,122 +3891,162 @@ "statuses": "~2.0.2", "unpipe": "~1.0.0" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } + "engines": { + "node": ">= 0.8" } }, - "find-up": { + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat-cache": { + "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "requires": { + "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" } }, - "flatted": { + "node_modules/flatted": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true }, - "form-data": { + "node_modules/form-data": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" } }, - "formidable": { + "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, - "requires": { + "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "forwarded": { + "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "fs-minipass": { + "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { + "dependencies": { "minipass": "^3.0.0" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "fs.realpath": { + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "gauge": { + "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", "console-control-strings": "^1.0.0", @@ -2898,25 +4056,34 @@ "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" } }, - "gensync": { + "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-intrinsic": { + "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", @@ -2927,395 +4094,586 @@ "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "get-package-type": { + "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.0.0" + } }, - "get-proto": { + "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "requires": { + "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "get-stream": { + "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "globals": { + "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "gopd": { + "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "handlebars": { + "node_modules/handlebars": { "version": "4.7.9", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "has-flag": { + "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } }, - "has-symbols": { + "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "has-tostringtag": { + "node_modules/has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "requires": { + "dependencies": { "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-unicode": { + "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, - "hasown": { + "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "requires": { + "dependencies": { "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "html-escaper": { + "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-errors": { + "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "requires": { + "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "https-proxy-agent": { + "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { + "dependencies": { "agent-base": "6", "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "human-signals": { + "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "ieee754": { + "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "ignore": { + "node_modules/ignore": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4" + } }, - "import-fresh": { + "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "requires": { + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "import-local": { + "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, - "requires": { + "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.19" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ipaddr.js": { + "node_modules/ioredis": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, - "requires": { + "dependencies": { "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } }, - "is-generator-fn": { + "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "istanbul-lib-coverage": { + "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "istanbul-lib-instrument": { + "node_modules/istanbul-lib-instrument": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" } }, - "istanbul-lib-report": { + "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "requires": { + "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - } + "engines": { + "node": ">=10" } }, - "istanbul-lib-source-maps": { + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "requires": { + "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" } }, - "istanbul-reports": { + "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, - "requires": { + "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "iterare": { + "node_modules/iterare": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } }, - "jest": { + "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "requires": { + "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" }, - "dependencies": { - "jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "jest-changed-files": { + "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "requires": { + "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-circus": { + "node_modules/jest-circus": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -3336,14 +4694,17 @@ "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-config": { + "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", @@ -3366,110 +4727,151 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "jest-diff": { + "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-docblock": { + "node_modules/jest-docblock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "requires": { + "dependencies": { "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-each": { + "node_modules/jest-each": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "jest-util": "^29.7.0", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-environment-node": { + "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-get-type": { + "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "jest-haste-map": { + "node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "jest-leak-detector": { + "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "requires": { + "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-matcher-utils": { + "node_modules/jest-matcher-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-message-util": { + "node_modules/jest-message-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", @@ -3479,37 +4881,57 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-mock": { + "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-pnp-resolver": { + "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } }, - "jest-regex-util": { + "node_modules/jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "jest-resolve": { + "node_modules/jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", @@ -3519,24 +4941,30 @@ "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-resolve-dependencies": { + "node_modules/jest-resolve-dependencies": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "requires": { + "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-runner": { + "node_modules/jest-runner": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -3558,14 +4986,17 @@ "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-runtime": { + "node_modules/jest-runtime": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/globals": "^29.7.0", @@ -3588,14 +5019,17 @@ "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-snapshot": { + "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", @@ -3616,28 +5050,47 @@ "natural-compare": "^1.4.0", "pretty-format": "^29.7.0", "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-util": { + "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-validate": { + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", @@ -3645,21 +5098,28 @@ "leven": "^3.1.0", "pretty-format": "^29.7.0" }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-watcher": { + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "requires": { + "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", @@ -3668,86 +5128,153 @@ "emittery": "^0.13.1", "jest-util": "^29.7.0", "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-worker": { + "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "js-tokens": { + "node_modules/jose": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.6.tgz", + "integrity": "sha512-HwMtbJjMw8rC8dUTwCNilHJD+fxTeKM3JV1eprSmTjS41qwXSSt6exJXgyPK1QOu0jB9eDYLESRDkB3qaT3jnw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsesc": { + "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } }, - "json-buffer": { + "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json5": { + "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "jsonwebtoken": { + "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", - "requires": { + "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", @@ -3759,312 +5286,393 @@ "ms": "^2.1.1", "semver": "^7.5.4" }, - "dependencies": { - "jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "requires": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "requires": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - } + "engines": { + "node": ">=12", + "npm": ">=6" } }, - "jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "requires": { + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, - "jws": { + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", - "requires": { + "dependencies": { "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, - "keyv": { + "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { + "dependencies": { "json-buffer": "3.0.1" } }, - "kleur": { + "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "leven": { + "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "levn": { + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "libphonenumber-js": { + "node_modules/libphonenumber-js": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.10.tgz", "integrity": "sha512-xJxrdqvbl2rtn2MaUJrUejz8J7/uZNC0V77oks2LxYrO/+ZtVpRmz+fEQMuu6VusnEB1fmpByiLS1WXecOnAnw==" }, - "lines-and-columns": { + "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "locate-path": { + "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.includes": { + "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, - "lodash.isboolean": { + "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "lodash.isinteger": { + "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, - "lodash.isnumber": { + "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, - "lodash.isplainobject": { + "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, - "lodash.isstring": { + "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, - "lodash.memoize": { + "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.once": { + "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "lru-cache": { + "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "yallist": "^3.0.2" - }, "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } + "yallist": "^3.0.2" } }, - "make-dir": { + "node_modules/lru-cache/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { + "dependencies": { "semver": "^6.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "make-error": { + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "makeerror": { + "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { + "dependencies": { "tmpl": "1.0.5" } }, - "math-intrinsics": { + "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } }, - "media-typer": { + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "methods": { + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime": { + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "minipass": { + "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } }, - "minizlib": { + "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { + "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "mkdirp": { + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { + "dependencies": { "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "multer": { + "node_modules/multer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", - "requires": { + "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", @@ -4072,262 +5680,394 @@ "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" } }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "negotiator": { + "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node-addon-api": { + "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, - "node-fetch": { + "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { + "dependencies": { "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node-int64": { + "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node-releases": { + "node_modules/node-releases": { "version": "2.0.51", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + } }, - "nopt": { + "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { + "dependencies": { "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "npmlog": { + "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", "gauge": "^3.0.0", "set-blocking": "^2.0.0" } }, - "object-assign": { + "node_modules/oauth4webapi": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", + "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } }, - "object-inspect": { + "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "on-exit-leak-free": { + "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } }, - "on-finished": { + "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "optionator": { + "node_modules/openid-client": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", + "integrity": "sha512-QSw0BA08piujetEwfZsHoTrDpMEha7GDZDicQqVwX4u0ChCjefvjDB++TZ8BTg76UpwhzIQgdvvfgfl3HpCSAw==", + "license": "MIT", + "dependencies": { + "jose": "^6.2.2", + "oauth4webapi": "^3.8.5" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { + "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parseurl": { + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } }, - "passport": { + "node_modules/passport": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "requires": { + "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-jwt": { + "node_modules/passport-jwt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "requires": { + "dependencies": { "jsonwebtoken": "^9.0.0", "passport-strategy": "^1.0.0" } }, - "passport-strategy": { + "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-to-regexp": { + "node_modules/path-to-regexp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, - "pause": { + "node_modules/pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, - "picocolors": { + "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pino": { + "node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", - "requires": { + "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", @@ -4339,304 +6079,537 @@ "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" } }, - "pino-abstract-transport": { + "node_modules/pino-abstract-transport": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "requires": { + "dependencies": { "split2": "^4.0.0" } }, - "pino-std-serializers": { + "node_modules/pino-std-serializers": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==" }, - "pirates": { + "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "pkg-dir": { + "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { + "dependencies": { "find-up": "^4.0.0" }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "engines": { + "node": ">=8" } }, - "prelude-ls": { + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "prettier": { + "node_modules/prettier": { "version": "3.9.6", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "dev": true + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "pretty-format": { + "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "requires": { + "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "prisma": { + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", - "dev": true, - "requires": { - "@prisma/engines": "5.22.0", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { "fsevents": "2.3.3" } }, - "process-warning": { + "node_modules/process-warning": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", - "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==" + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, - "prompts": { + "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "requires": { + "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "proxy-addr": { + "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { + "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "punycode": { + "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "pure-rand": { + "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] }, - "qs": { + "node_modules/qs": { "version": "6.14.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "requires": { + "dependencies": { "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "quick-format-unescaped": { + "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, - "range-parser": { + "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } }, - "raw-body": { + "node_modules/raw-body": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "requires": { + "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "react-is": { + "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, - "readable-stream": { + "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "real-require": { + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } }, - "reflect-metadata": { + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve": { + "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, - "requires": { + "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "resolve.exports": { + "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "rxjs": { + "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "requires": { + "dependencies": { "tslib": "^2.1.0" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "safe-stable-stringify": { + "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "semver": { + "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==" + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "send": { + "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "requires": { + "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -4651,243 +6624,327 @@ "range-parser": "~1.2.1", "statuses": "~2.0.2" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - } + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-static": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "requires": { + "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, - "setprototypeof": { + "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "side-channel": { + "node_modules/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-list": { + "node_modules/side-channel-list": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-map": { + "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-weakmap": { + "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "sonic-boom": { + "node_modules/sonic-boom": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", - "requires": { + "dependencies": { "atomic-sleep": "^1.0.0" } }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "split2": { + "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-utils": { + "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "statuses": { + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } }, - "streamsearch": { + "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } }, - "string-length": { + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "strtok3": { + "node_modules/strtok3": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", - "requires": { + "dependencies": { "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "superagent": { + "node_modules/superagent": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", "dev": true, - "requires": { + "dependencies": { "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", "debug": "^4.3.7", @@ -4898,66 +6955,93 @@ "mime": "2.6.0", "qs": "^6.14.1" }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - } + "engines": { + "node": ">=14.18.0" } }, - "supertest": { + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", "dev": true, - "requires": { + "dependencies": { "cookie-signature": "^1.2.2", "methods": "^1.1.2", "superagent": "^10.3.0" }, - "dependencies": { - "cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "dev": true - } + "engines": { + "node": ">=14.18.0" } }, - "supports-color": { + "node_modules/supertest/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "swagger-ui-dist": { + "node_modules/swagger-ui-dist": { "version": "5.17.14", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" }, - "swagger-ui-express": { + "node_modules/swagger-ui-express": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "requires": { + "dependencies": { "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" } }, - "tar": { + "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "requires": { + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", @@ -4965,104 +7049,134 @@ "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } + "engines": { + "node": ">=10" } }, - "test-exclude": { + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "thread-stream": { + "node_modules/thread-stream": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz", "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==", - "requires": { + "dependencies": { "real-require": "^0.2.0" } }, - "tinyglobby": { + "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, - "requires": { + "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" }, - "dependencies": { - "picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true - } + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "tmpl": { + "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "toidentifier": { + "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } }, - "token-types": { + "node_modules/token-types": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", - "requires": { + "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "tr46": { + "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "tree-kill": { + "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true + "dev": true, + "bin": { + "tree-kill": "cli.js" + } }, - "ts-api-utils": { + "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } }, - "ts-jest": { + "node_modules/ts-jest": { "version": "29.4.12", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", "dev": true, - "requires": { + "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", "handlebars": "^4.7.9", @@ -5073,21 +7187,60 @@ "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, - "dependencies": { - "type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true } } }, - "ts-node": { + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "requires": { + "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", @@ -5101,14 +7254,36 @@ "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "ts-node-dev": { + "node_modules/ts-node-dev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, - "requires": { + "dependencies": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", "minimist": "^1.2.6", @@ -5120,287 +7295,418 @@ "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "tsconfig": { + "node_modules/ts-node-dev/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node-dev/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/tsconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", "dev": true, - "requires": { + "dependencies": { "@types/strip-bom": "^3.0.0", "@types/strip-json-comments": "0.0.30", "strip-bom": "^3.0.0", "strip-json-comments": "^2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } } }, - "tslib": { + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "type-is": { + "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "typedarray": { + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "typescript": { + "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "uglify-js": { + "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "optional": true + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, - "uid": { + "node_modules/uid": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "requires": { + "dependencies": { "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "uint8array-extras": { + "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==" + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "undici-types": { + "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==" }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } }, - "update-browserslist-db": { + "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "uuid": { + "node_modules/uuid": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", - "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==" + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } }, - "v8-compile-cache-lib": { + "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "v8-to-istanbul": { + "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "validator": { + "node_modules/validator": { "version": "13.15.35", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", - "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==" + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "engines": { + "node": ">= 0.10" + } }, - "vary": { + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "walker": { + "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { + "dependencies": { "makeerror": "1.0.12" } }, - "webidl-conversions": { + "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "whatwg-url": { + "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { + "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "word-wrap": { + "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "wordwrap": { + "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yargs": { + "node_modules/yargs": { "version": "17.7.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -5408,25 +7714,40 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + } }, - "yn": { + "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 48b8469..2cc6cd2 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "scripts": { "start": "node dist/main.js", "start:dev": "ts-node-dev --respawn --pretty --transpile-only src/main.ts", + "start:debug": "nest start --debug --watch", "build": "tsc -p tsconfig.json", "lint": "eslint \"src/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", @@ -37,6 +38,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.6.1", + "jose": "^6.2.6", + "openid-client": "^6.5.0", "jsonwebtoken": "^9.0.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -47,6 +50,9 @@ "uuid": "^11.1.0" }, "devDependencies": { + "@eslint/js": "^9.21.0", + "@nestjs/testing": "^10.4.22", + "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", "@types/jest": "^29.5.14", "@types/node": "^22.13.10", @@ -56,7 +62,6 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "eslint": "^9.21.0", - "@eslint/js": "^9.21.0", "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.5.3", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..5b753de --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,17 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + // only run Playwright tests in the integration folder to avoid running Jest unit tests + testDir: './tests/integration', + timeout: 30 * 1000, + expect: { + timeout: 5000, + }, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [["list"], ["./tests/reporter/custom-reporter.js"]], + use: { + baseURL: process.env.BASE_URL || 'http://localhost:3000', + }, +}); diff --git a/postman/RayLab.postman_collection.json b/postman/RayLab.postman_collection.json new file mode 100644 index 0000000..f94df2a --- /dev/null +++ b/postman/RayLab.postman_collection.json @@ -0,0 +1,58 @@ +{ + "info": { + "_postman_id": "e1e605cc-a2c6-4410-a7aa-4692b594d42d", + "name": "RayLab", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "45313893" + }, + "item": [ + { + "name": "User", + "item": [ + { + "name": "Create User", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Authorization", + "value": "Bearer ", + "description": "Bearer ", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"username\": \"rayyan\",\r\n \"email\": \"rayyan@example.com\",\r\n \"password\": \"Password123!\",\r\n \"fullName\": \"Rayyan\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:3000/api/v1/users", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "3000", + "path": [ + "api", + "v1", + "users" + ] + } + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/20260730165554_permission/migration.sql b/prisma/migrations/20260730165554_permission/migration.sql new file mode 100644 index 0000000..23e8e28 --- /dev/null +++ b/prisma/migrations/20260730165554_permission/migration.sql @@ -0,0 +1,93 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "username" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Role" ( + "id" TEXT NOT NULL, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Role_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Permission" ( + "id" TEXT NOT NULL, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Permission_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserRole" ( + "userId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("userId","roleId") +); + +-- CreateTable +CREATE TABLE "RolePermission" ( + "roleId" TEXT NOT NULL, + "permissionId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "RolePermission_pkey" PRIMARY KEY ("roleId","permissionId") +); + +-- CreateTable +CREATE TABLE "UserPermission" ( + "userId" TEXT NOT NULL, + "permissionId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserPermission_pkey" PRIMARY KEY ("userId","permissionId") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Role_code_key" ON "Role"("code"); + +-- CreateIndex +CREATE UNIQUE INDEX "Permission_code_key" ON "Permission"("code"); + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260801084138_change/migration.sql b/prisma/migrations/20260801084138_change/migration.sql new file mode 100644 index 0000000..f73805a --- /dev/null +++ b/prisma/migrations/20260801084138_change/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - Added the required column `assignedBy` to the `RolePermission` table without a default value. This is not possible if the table is not empty. + - Added the required column `assignedBy` to the `UserPermission` table without a default value. This is not possible if the table is not empty. + - Added the required column `assignedBy` to the `UserRole` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "RolePermission" ADD COLUMN "assignedBy" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "UserPermission" ADD COLUMN "assignedBy" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "UserRole" ADD COLUMN "assignedBy" TEXT NOT NULL; diff --git a/prisma/migrations/20260801101438_add_role_is_default/migration.sql b/prisma/migrations/20260801101438_add_role_is_default/migration.sql new file mode 100644 index 0000000..dabc294 --- /dev/null +++ b/prisma/migrations/20260801101438_add_role_is_default/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Role" ADD COLUMN "isDefault" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20260801101841_add_authentik_id/migration.sql b/prisma/migrations/20260801101841_add_authentik_id/migration.sql new file mode 100644 index 0000000..aa49398 --- /dev/null +++ b/prisma/migrations/20260801101841_add_authentik_id/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[authentikId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "authentikId" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikId_key" ON "User"("authentikId"); diff --git a/prisma/migrations/20260801104311_add_deleted_at/migration.sql b/prisma/migrations/20260801104311_add_deleted_at/migration.sql new file mode 100644 index 0000000..a9d12c0 --- /dev/null +++ b/prisma/migrations/20260801104311_add_deleted_at/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `password` on the `User` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "User" DROP COLUMN "password", +ADD COLUMN "deletedAt" TIMESTAMP(3), +ADD COLUMN "lastSeenAt" TIMESTAMP(3); diff --git a/prisma/migrations/20260801132311_add_authentik_fields/migration.sql b/prisma/migrations/20260801132311_add_authentik_fields/migration.sql new file mode 100644 index 0000000..32ee861 --- /dev/null +++ b/prisma/migrations/20260801132311_add_authentik_fields/migration.sql @@ -0,0 +1,15 @@ +/* + Warnings: + + - A unique constraint covering the columns `[authentikUserId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[authentikSubject]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "lastSyncedAt" SET DATA TYPE TIMESTAMP(3); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikUserId_key" ON "User"("authentikUserId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikSubject_key" ON "User"("authentikSubject"); diff --git a/prisma/migrations/20260802_add_authentik_fields/migration.sql b/prisma/migrations/20260802_add_authentik_fields/migration.sql new file mode 100644 index 0000000..fc9ffb9 --- /dev/null +++ b/prisma/migrations/20260802_add_authentik_fields/migration.sql @@ -0,0 +1,17 @@ +-- Migration: add_authentik_fields + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "authentikUserId" TEXT; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "authentikSubject" TEXT; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "lastSyncedAt" TIMESTAMP; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "syncStatus" TEXT; + +-- Optional: add unique constraints if desired +-- DO NOT add unique constraints without verifying existing data +-- ALTER TABLE "User" ADD CONSTRAINT "User_authentikUserId_key" UNIQUE ("authentikUserId"); diff --git a/prisma/migrations/20260803_add_storage/migration.sql b/prisma/migrations/20260803_add_storage/migration.sql new file mode 100644 index 0000000..d7df7a3 --- /dev/null +++ b/prisma/migrations/20260803_add_storage/migration.sql @@ -0,0 +1,7 @@ +-- Migration: add_storage + +ALTER TABLE "User" + ADD COLUMN IF NOT EXISTS "storageQuota" bigint NOT NULL DEFAULT 10737418240; + +ALTER TABLE "User" + ADD COLUMN IF NOT EXISTS "storageUsed" bigint NOT NULL DEFAULT 0; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 19f49c7..a6c5140 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,13 +11,99 @@ datasource db { } model User { - id String @id @default(uuid()) - name String - email String @unique - password String - status String @default("ACTIVE") - metadata Json? - created_at DateTime @default(now()) - updated_at DateTime @updatedAt - deleted_at DateTime? + id String @id @default(uuid()) + + authentikId String? @unique + authentikUserId String? @unique + authentikSubject String? @unique + + username String @unique + email String @unique + + + isActive Boolean @default(true) + + // storage (in bytes) + storageQuota BigInt @default(10737418240) + storageUsed BigInt @default(0) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + lastSeenAt DateTime? + lastSyncedAt DateTime? + syncStatus String? + + roles UserRole[] + permissions UserPermission[] + } + +model Role { + id String @id @default(uuid()) + + code String @unique + name String + description String? + + isDefault Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + users UserRole[] + permissions RolePermission[] +} + +model Permission { + id String @id @default(uuid()) + + code String @unique + name String + description String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + roles RolePermission[] + users UserPermission[] +} + +model UserRole { + userId String + roleId String + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([userId, roleId]) +} + +model RolePermission { + roleId String + permissionId String + + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([roleId, permissionId]) +} + +model UserPermission { + userId String + permissionId String + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([userId, permissionId]) +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 01cb319..e6e9b1a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { IdentityModule } from './modules/identity/identity.module'; +import { AuthModule } from './modules/auth/auth.module'; @Module({ imports: [ @@ -10,6 +11,7 @@ import { IdentityModule } from './modules/identity/identity.module'; }), IdentityModule, + AuthModule, ], }) export class AppModule {} \ No newline at end of file diff --git a/src/common/constants/permission.constants.ts b/src/common/constants/permission.constants.ts new file mode 100644 index 0000000..29c7547 --- /dev/null +++ b/src/common/constants/permission.constants.ts @@ -0,0 +1,16 @@ +export const PermissionType = { + USER_CREATE: 'USER_CREATE', + USER_READ: 'USER_READ', + USER_UPDATE: 'USER_UPDATE', + USER_DELETE: 'USER_DELETE', + + ROLE_CREATE: 'ROLE_CREATE', + ROLE_READ: 'ROLE_READ', + ROLE_UPDATE: 'ROLE_UPDATE', + ROLE_DELETE: 'ROLE_DELETE', + + PERMISSION_CREATE: 'PERMISSION_CREATE', + PERMISSION_READ: 'PERMISSION_READ', + PERMISSION_UPDATE: 'PERMISSION_UPDATE', + PERMISSION_DELETE: 'PERMISSION_DELETE', +} as const; \ No newline at end of file diff --git a/src/core/auth/guards/jwt-auth.guard.ts b/src/core/auth/guards/jwt-auth.guard.ts index d29b625..7b657bb 100644 --- a/src/core/auth/guards/jwt-auth.guard.ts +++ b/src/core/auth/guards/jwt-auth.guard.ts @@ -4,17 +4,23 @@ import { ExecutionContext, UnauthorizedException, } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; import { Request } from 'express'; +import { createRemoteJWKSet, jwtVerify } from 'jose'; +import { IdentityData } from '../interfaces/identity-data'; +import * as jwt from 'jsonwebtoken'; +/** + * JwtAuthGuard verifies JWTs issued by the external Identity Provider (Authentik) + * using JWKS (RS256). It also accepts internal RayLab JWTs signed with a local secret. + */ @Injectable() export class JwtAuthGuard implements CanActivate { - constructor( - private readonly jwtService: JwtService, - ) {} + private jwks: ReturnType | null = null; + + constructor() {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); + const request = context.switchToHttp().getRequest(); const authHeader = request.headers.authorization; @@ -28,13 +34,51 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('Invalid authorization header.'); } + const jwksUri = process.env.AUTHENTIK_JWKS_URI; + + // First try verifying with external JWKS (Authentik) + if (jwksUri) { + try { + if (!this.jwks) this.jwks = createRemoteJWKSet(new URL(jwksUri)); + + const { payload } = await jwtVerify(token, this.jwks, { + issuer: process.env.AUTHENTIK_ISSUER, + audience: process.env.AUTHENTIK_AUDIENCE, + }); + + const identity = new IdentityData( + payload.sub as string, + (payload as any).preferred_username as string | undefined, + (payload as any).email as string | undefined, + payload as Record, + ); + + request.identity = identity; + return true; + } catch (err) { + // ignore and try internal verification + } + } + + // Fallback: verify with internal symmetric secret + const secret = process.env.RAYLAB_JWT_SECRET; + if (!secret) { + throw new UnauthorizedException('Invalid or expired token.'); + } + try { - const payload = await this.jwtService.verifyAsync(token); + const payload = jwt.verify(token, secret) as any; - request['user'] = payload; + const identity = new IdentityData( + payload.sub as string, + payload.preferred_username as string | undefined, + payload.email as string | undefined, + payload as Record, + ); + request.identity = identity; return true; - } catch { + } catch (err: any) { throw new UnauthorizedException('Invalid or expired token.'); } } diff --git a/src/core/auth/interfaces/authenticated-request.interface.ts b/src/core/auth/interfaces/authenticated-request.interface.ts index 440f68b..03019a1 100644 --- a/src/core/auth/interfaces/authenticated-request.interface.ts +++ b/src/core/auth/interfaces/authenticated-request.interface.ts @@ -1,12 +1,12 @@ import { Request } from 'express'; - -export interface JwtPayload { - sub: string; - email: string; - role: string; - permissions: string[]; -} +import { UserData } from '../../../modules/identity/domain/entities/user.entity'; +import { IdentityData } from './identity-data'; export interface AuthenticatedRequest extends Request { - user: JwtPayload; + // Identity comes from the external Identity Provider (Authentik) + // JwtAuthGuard must set request.identity = payload + identity?: IdentityData; + + // After CurrentUserGuard resolves the user from repository, it must set request.currentUser = User Domain + currentUser?: UserData; } \ No newline at end of file diff --git a/src/core/auth/interfaces/identity-data.ts b/src/core/auth/interfaces/identity-data.ts new file mode 100644 index 0000000..640aca7 --- /dev/null +++ b/src/core/auth/interfaces/identity-data.ts @@ -0,0 +1,8 @@ +export class IdentityData { + constructor( + public readonly sub: string, + public readonly preferred_username?: string, + public readonly email?: string, + public readonly claims?: Record, + ) {} +} diff --git a/src/main.ts b/src/main.ts index 6449bee..f6e22bd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,7 @@ async function bootstrap() { const config = app.get(ConfigService); - app.setGlobalPrefix('api'); + app.setGlobalPrefix(''); app.useGlobalPipes( new ValidationPipe({ @@ -22,8 +22,7 @@ async function bootstrap() { app.enableCors(); - const swaggerEnabled = - config.get('SWAGGER_ENABLED') === 'true'; + const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; if (swaggerEnabled) { const swaggerConfig = new DocumentBuilder() @@ -35,7 +34,7 @@ async function bootstrap() { const document = SwaggerModule.createDocument(app, swaggerConfig); - SwaggerModule.setup('docs', app, document); + SwaggerModule.setup('ApiList', app, document); } const port = config.get('PORT') || 3000; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts new file mode 100644 index 0000000..5ef7553 --- /dev/null +++ b/src/modules/auth/auth.controller.ts @@ -0,0 +1,65 @@ + +import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } from '@nestjs/common'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; +import { AuthService } from './auth.service'; +import { Response, Request } from 'express'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Get('login') + @ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }) + async login(@Query('returnTo') returnTo: string | undefined, @Res() res: Response) { + const redirect = await this.authService.createAuthorizationRedirect(returnTo); + return res.redirect(302, redirect); + } + + @Get('callback') + @ApiOperation({ summary: 'OIDC callback endpoint' }) + async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) { + const result = await this.authService.handleCallback(code, state); + + // set cookies + const cookieOptions: any = { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/', + }; + + // access token cookie (internal JWT) + res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); + + // refresh token cookie + res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); + + return res.redirect(302, result.returnTo || '/'); + } + + @Post('logout') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Logout (invalidate internal session and redirect to identity provider logout)' }) + async logout(@Req() req: Request, @Res() res: Response) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const redirect = await this.authService.logout(refreshToken); + return res.redirect(302, redirect); + } + + @Post('refresh') + @ApiOperation({ summary: 'Refresh internal JWT using internal refresh token' }) + async refresh(@Req() req: Request) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const result = await this.authService.refresh(refreshToken); + return { success: true, data: result }; + } + + @Get('me') + @ApiOperation({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }) + async me(@Req() req: Request) { + const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, '')); + const user = await this.authService.me(token); + return { success: true, data: user }; + } +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..820594c --- /dev/null +++ b/src/modules/auth/auth.module.ts @@ -0,0 +1,46 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; +import { PrismaService } from '../../shared/prisma.service'; +import { IUser } from '../identity/domain/repositories/user.interface'; +import { PrismaUserRepository } from '../identity/infrastructure/repositories/prisma-user.repository'; +import { SyncIdentityHandler } from '../identity/application/handlers/user/sync-identity.handler'; +import { IRole } from '../identity/domain/repositories/role.interface'; +import { PrismaRoleRepository } from '../identity/infrastructure/repositories/prisma-role.repository'; +import { IAuthConfig } from '../identity/application/config/i-auth-config'; +import { EnvAuthConfig } from '../identity/application/config/env-auth-config'; +import { RedisPkceStore } from './pkce/redis-pkce.store'; +import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; + +@Module({ + imports: [ + ConfigModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (config: ConfigService) => ({ + secret: config.get('RAYLAB_JWT_SECRET') || 'raylab-secret', + signOptions: { algorithm: 'HS256' }, + }), + inject: [ConfigService], + }), + ], + controllers: [AuthController], + providers: [ + AuthService, + PrismaService, + PrismaUserRepository, + PrismaRoleRepository, + SyncIdentityHandler, + { provide: IUser, useClass: PrismaUserRepository }, + { provide: IRole, useClass: PrismaRoleRepository }, + { provide: IAuthConfig, useClass: EnvAuthConfig }, + + // In-memory PKCE and Refresh stores (Redis removed) + RedisPkceStore, + InMemoryRefreshStore, + ], + exports: [AuthService], +}) +export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts new file mode 100644 index 0000000..f488309 --- /dev/null +++ b/src/modules/auth/auth.service.ts @@ -0,0 +1,193 @@ +import { Injectable, UnauthorizedException, Inject } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { JwtService } from '@nestjs/jwt'; +import { SyncIdentityHandler } from '../identity/application/handlers/user/sync-identity.handler'; +import { IUser } from '../identity/domain/repositories/user.interface'; +import { PrismaService } from '../../shared/prisma.service'; +import { RedisPkceStore } from './pkce/redis-pkce.store'; +import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; + +import { Issuer, generators, TokenSet } from 'openid-client'; + +@Injectable() +export class AuthService { + constructor( + private readonly config: ConfigService, + private readonly jwtService: JwtService, + private readonly prisma: PrismaService, + @Inject(IUser) private readonly userRepository: IUser, + private readonly syncIdentityHandler: SyncIdentityHandler, + private readonly pkceStore: RedisPkceStore, + private readonly refreshStore: InMemoryRefreshStore, + ) {} + + private issuer: any = null; + private client: any = null; + + private async getIssuer() { + if (this.issuer) return this.issuer; + const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); + if (!issuerUrl) throw new Error('AUTHENTIK_ISSUER not configured'); + this.issuer = await Issuer.discover(issuerUrl); + return this.issuer; + } + + private async getClient() { + if (this.client) return this.client; + const issuer = await this.getIssuer(); + const clientId = this.config.get('AUTHENTIK_CLIENT_ID'); + const clientSecret = this.config.get('AUTHENTIK_CLIENT_SECRET'); + if (!clientId) throw new Error('AUTHENTIK_CLIENT_ID not configured'); + this.client = new issuer.Client({ client_id: clientId, client_secret: clientSecret }); + return this.client; + } + + async createAuthorizationRedirect(returnTo?: string) { + const client = await this.getClient(); + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + + const state = require('crypto').randomUUID(); + const code_verifier = generators.codeVerifier(); + const code_challenge = await generators.codeChallenge(code_verifier); + const nonce = generators.nonce(); + + await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); + + const url = client.authorizationUrl({ + redirect_uri: redirectUri, + scope: this.config.get('AUTHENTIK_DEFAULT_SCOPE') || 'openid email profile', + response_type: 'code', + code_challenge, + code_challenge_method: 'S256', + state, + nonce, + }); + + return url; + } + + async handleCallback(code: string, state: string) { + const client = await this.getClient(); + const pkce = await this.pkceStore.get(state); + if (!pkce) throw new UnauthorizedException('Invalid or expired state'); + + // remove one-time state + await this.pkceStore.remove(state); + + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + + // exchange code + const tokenSet: TokenSet = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); + + // verify id_token and get claims + const claims = tokenSet.claims(); + + // fetch userinfo if available + let userInfo = null; + try { + if (tokenSet.access_token && client.userinfo) { + userInfo = await client.userinfo(tokenSet.access_token); + } + } catch (e) { + // ignore + } + + const identity = { + sub: userInfo?.sub || claims.sub || null, + preferred_username: userInfo?.preferred_username || userInfo?.username || userInfo?.email || claims.preferred_username || claims.email, + email: userInfo?.email || claims.email, + raw: { tokenSet, userInfo, claims }, + } as any; + + // Sync identity to local user (create if needed) + const domainUser = await this.syncIdentityHandler.execute(identity as any); + + // ensure active/not deleted + if (!domainUser.isActive) throw new UnauthorizedException('User is not active'); + if (domainUser.deletedAt) throw new UnauthorizedException('User is deleted'); + + // create internal JWT + const jwtPayload = { + sub: domainUser.id, + preferred_username: domainUser.username, + email: domainUser.email, + } as any; + + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign(jwtPayload, { expiresIn }); + + // create internal refresh token + const refreshToken = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days + + await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); + + return { + accessToken: access, + refreshToken, + expiresIn, + refreshTtl, + user: { + id: domainUser.id, + username: domainUser.username, + email: domainUser.email, + roles: domainUser.roles || [], + }, + returnTo: pkce.returnTo, + }; + } + + async refresh(refreshToken?: string) { + if (!refreshToken) throw new UnauthorizedException('Missing refresh token'); + const data = await this.refreshStore.get(refreshToken); + if (!data) throw new UnauthorizedException('Invalid refresh token'); + + const userId = data.userId; + // load user + const domainUser = await this.userRepository.findById(userId); + if (!domainUser) throw new UnauthorizedException('User not found'); + if (!domainUser.isActive) throw new UnauthorizedException('User is not active'); + + // rotate refresh token + await this.refreshStore.del(refreshToken); + const newRefresh = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); + await this.refreshStore.set(newRefresh, { userId }, refreshTtl); + + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign({ sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email }, { expiresIn }); + + return { accessToken: access, refreshToken: newRefresh, expiresIn, refreshTtl }; + } + + async logout(refreshToken?: string) { + if (refreshToken) { + await this.refreshStore.del(refreshToken); + } + + const issuer = await this.getIssuer(); + const endSession = issuer.metadata.end_session_endpoint; + const postLogout = this.config.get('APP_URL') || '/'; + + if (endSession) { + // Redirect to identity provider logout + const url = new URL(endSession); + if (postLogout) url.searchParams.set('post_logout_redirect_uri', postLogout); + return url.toString(); + } + + return postLogout; + } + + async me(token?: string) { + if (!token) throw new UnauthorizedException('Missing token'); + try { + const payload: any = this.jwtService.verify(token); + const user = await this.userRepository.findById(payload.sub); + if (!user) throw new UnauthorizedException('User not found'); + return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; + } catch (e) { + throw new UnauthorizedException('Invalid token'); + } + } +} diff --git a/src/modules/auth/dto/login.dto.ts b/src/modules/auth/dto/login.dto.ts new file mode 100644 index 0000000..d3c2d25 --- /dev/null +++ b/src/modules/auth/dto/login.dto.ts @@ -0,0 +1,2 @@ +// Login DTO removed. Password grant has been removed in favor of Authorization Code + PKCE flow. +// Formerly contained username/password properties. diff --git a/src/modules/auth/pkce/redis-pkce.store.ts b/src/modules/auth/pkce/redis-pkce.store.ts new file mode 100644 index 0000000..2124c59 --- /dev/null +++ b/src/modules/auth/pkce/redis-pkce.store.ts @@ -0,0 +1,42 @@ +import { Injectable, OnModuleDestroy } from '@nestjs/common'; + +type PkceEntry = { code_verifier: string; nonce: string; returnTo?: string; expiresAt: number }; + +@Injectable() +export class RedisPkceStore implements OnModuleDestroy { + // In-memory PKCE store replacing Redis-backed implementation + private map = new Map(); + private cleanupInterval?: NodeJS.Timeout; + + constructor() { + // periodic cleanup + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) this.map.delete(k); + } + }, 60 * 1000); + } + + private key(state: string) { return state; } + + async save(state: string, data: { code_verifier: string; nonce: string; returnTo?: string }, ttlSeconds = 300) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(this.key(state), { ...data, expiresAt }); + } + + async get(state: string) { + const v = this.map.get(this.key(state)); + if (!v) return null; + if (v.expiresAt <= Date.now()) { this.map.delete(this.key(state)); return null; } + return { code_verifier: v.code_verifier, nonce: v.nonce, returnTo: v.returnTo }; + } + + async remove(state: string) { + this.map.delete(this.key(state)); + } + + onModuleDestroy() { + if (this.cleanupInterval) clearInterval(this.cleanupInterval); + } +} diff --git a/src/modules/auth/refresh/inmemory-refresh.store.ts b/src/modules/auth/refresh/inmemory-refresh.store.ts new file mode 100644 index 0000000..5917283 --- /dev/null +++ b/src/modules/auth/refresh/inmemory-refresh.store.ts @@ -0,0 +1,38 @@ +import { Injectable, OnModuleDestroy } from '@nestjs/common'; + +type RefreshEntry = { userId: string; expiresAt: number }; + +@Injectable() +export class InMemoryRefreshStore implements OnModuleDestroy { + private map = new Map(); + private cleanupInterval?: NodeJS.Timeout; + + constructor() { + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) this.map.delete(k); + } + }, 60 * 1000); + } + + async set(token: string, data: { userId: string }, ttlSeconds: number) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(token, { userId: data.userId, expiresAt }); + } + + async get(token: string) { + const v = this.map.get(token); + if (!v) return null; + if (v.expiresAt <= Date.now()) { this.map.delete(token); return null; } + return { userId: v.userId }; + } + + async del(token: string) { + this.map.delete(token); + } + + onModuleDestroy() { + if (this.cleanupInterval) clearInterval(this.cleanupInterval); + } +} diff --git a/src/modules/authorization/presentation/guards/permission.guard.ts b/src/modules/authorization/presentation/guards/permission.guard.ts index acca065..bee1027 100644 --- a/src/modules/authorization/presentation/guards/permission.guard.ts +++ b/src/modules/authorization/presentation/guards/permission.guard.ts @@ -2,6 +2,8 @@ import { Injectable, CanActivate, ExecutionContext, + UnauthorizedException, + ForbiddenException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @@ -25,12 +27,20 @@ export class PermissionGuard implements CanActivate { return true; } - const request = context.switchToHttp().getRequest(); + const request = context.switchToHttp().getRequest() as any; - const user = request.user; + const user = request.currentUser; - return permissions.every(permission => - user.permissions.includes(permission), - ); + if (!user) { + throw new UnauthorizedException('Current user is missing.'); + } + + const hasAll = permissions.every((permission) => { + return user.hasPermission(permission); + }); + + if (!hasAll) throw new ForbiddenException('Insufficient permissions.'); + + return true; } } \ No newline at end of file diff --git a/src/modules/identity/application/README.md b/src/modules/identity/application/README.md index e01622b..68f05f1 100644 --- a/src/modules/identity/application/README.md +++ b/src/modules/identity/application/README.md @@ -1,12 +1 @@ -modules/identity/application/ - -Penjelasan: -Layer application berisi use-case (services/commands/queries) yang mengorkestrasi domain dan infrastruktur. - -Contoh file: -- services/get-user.service.ts -- commands/create-user.command.ts - -Aturan: -- Application service boleh memanggil repository interface, domain services, dan event publisher. -- Application menangani transaksi jika diperlukan. +ini adalah manager, semua kegiatan diarahkan dari sini, disini bukan mengakses db, menghitung, dll, dari controller akan masuk ke dalam sini dan dijalankan prosesnya, tapi tidak tau http dan databasenya. \ No newline at end of file diff --git a/src/modules/identity/application/config/env-auth-config.ts b/src/modules/identity/application/config/env-auth-config.ts new file mode 100644 index 0000000..74b24ed --- /dev/null +++ b/src/modules/identity/application/config/env-auth-config.ts @@ -0,0 +1,15 @@ +import { IAuthConfig } from './i-auth-config'; + +export class EnvAuthConfig implements IAuthConfig { + autoCreateUser(): boolean { + return (process.env.AUTH_AUTO_CREATE_USER ?? 'true') === 'true'; + } + + syncEmail(): boolean { + return (process.env.AUTH_SYNC_EMAIL ?? 'true') === 'true'; + } + + syncUsername(): boolean { + return (process.env.AUTH_SYNC_USERNAME ?? 'false') === 'true'; + } +} diff --git a/src/modules/identity/application/config/i-auth-config.ts b/src/modules/identity/application/config/i-auth-config.ts new file mode 100644 index 0000000..8e1d6a5 --- /dev/null +++ b/src/modules/identity/application/config/i-auth-config.ts @@ -0,0 +1,5 @@ +export abstract class IAuthConfig { + abstract autoCreateUser(): boolean; + abstract syncEmail(): boolean; + abstract syncUsername(): boolean; +} diff --git a/src/modules/identity/application/handlers/README.MD b/src/modules/identity/application/handlers/README.MD new file mode 100644 index 0000000..a0639ca --- /dev/null +++ b/src/modules/identity/application/handlers/README.MD @@ -0,0 +1 @@ +semua method disini didapat dari interface, itu ada di domain/repositories. \ No newline at end of file diff --git a/src/modules/identity/application/handlers/create-user.handler.ts b/src/modules/identity/application/handlers/create-user.handler.ts deleted file mode 100644 index 58a314b..0000000 --- a/src/modules/identity/application/handlers/create-user.handler.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Injectable, - ConflictException, -} from '@nestjs/common'; - -import { CreateUserDto } from '../../presentation/dto/create-user.dto'; -import { User } from '../../domain/entities/user.entity'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; - -@Injectable() -export class CreateUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(dto: CreateUserDto): Promise { - const exists = await this.userRepository.existsByEmail(dto.email); - - if (exists) { - throw new ConflictException('Email already exists.'); - } - - const user = User.create({ - name: dto.name, - email: dto.email, - password: dto.password, - metadata: dto.metadata, - }); - - await this.userRepository.create(user); - - // TODO: - // this.eventDispatcher.publish(new UserCreatedEvent(user)); - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/delete-user.handler.ts b/src/modules/identity/application/handlers/delete-user.handler.ts deleted file mode 100644 index be70def..0000000 --- a/src/modules/identity/application/handlers/delete-user.handler.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - Injectable, - NotFoundException, -} from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; - -@Injectable() -export class DeleteUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(id: string): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - user.delete(); - - await this.userRepository.update(user); - - // TODO: - // Publish UserDeletedEvent - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/find-user.handler.ts b/src/modules/identity/application/handlers/find-user.handler.ts deleted file mode 100644 index 5942fb4..0000000 --- a/src/modules/identity/application/handlers/find-user.handler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - Injectable, - NotFoundException, -} from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class FindUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(id: string): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/find-users.handler.ts b/src/modules/identity/application/handlers/find-users.handler.ts deleted file mode 100644 index c7b3af1..0000000 --- a/src/modules/identity/application/handlers/find-users.handler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class FindUsersHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(): Promise { - return await this.userRepository.findAll(); - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/permission/create-permission.handler.ts b/src/modules/identity/application/handlers/permission/create-permission.handler.ts new file mode 100644 index 0000000..ebcd193 --- /dev/null +++ b/src/modules/identity/application/handlers/permission/create-permission.handler.ts @@ -0,0 +1,21 @@ +import { Injectable, ConflictException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; +import { PermissionData } from '../../../domain/entities/permission.entity'; + +@Injectable() +export class CreatePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(dto: any) { + const perm = PermissionData.restore({ + id: crypto.randomUUID(), + code: dto.code || dto.name, + name: dto.name, + description: dto.description || '', + createdAt: new Date(), + updatedAt: new Date(), + } as any); + + return this.permissionRepository.create(perm); + } +} diff --git a/src/modules/identity/application/handlers/permission/delete-permission.handler.ts b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts new file mode 100644 index 0000000..cde347e --- /dev/null +++ b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class DeletePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string) { + const p = await this.permissionRepository.getById(id); + if (!p) throw new NotFoundException('Permission not found.'); + + await this.permissionRepository.delete(id); + } +} diff --git a/src/modules/identity/application/handlers/permission/get-permission.handler.ts b/src/modules/identity/application/handlers/permission/get-permission.handler.ts new file mode 100644 index 0000000..c7a1c8c --- /dev/null +++ b/src/modules/identity/application/handlers/permission/get-permission.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class GetPermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string) { + return this.permissionRepository.getById(id); + } +} diff --git a/src/modules/identity/application/handlers/permission/get-permissions.handler.ts b/src/modules/identity/application/handlers/permission/get-permissions.handler.ts new file mode 100644 index 0000000..309301b --- /dev/null +++ b/src/modules/identity/application/handlers/permission/get-permissions.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class GetPermissionsHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(query: { page?: number; limit?: number; search?: string }) { + return this.permissionRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +} diff --git a/src/modules/identity/application/handlers/permission/update-permission.handler.ts b/src/modules/identity/application/handlers/permission/update-permission.handler.ts new file mode 100644 index 0000000..7e642cf --- /dev/null +++ b/src/modules/identity/application/handlers/permission/update-permission.handler.ts @@ -0,0 +1,18 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class UpdatePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string, dto: any) { + const perm = await this.permissionRepository.getById(id); + if (!perm) throw new NotFoundException('Permission not found.'); + + if (dto.name) perm.changeName(dto.name); + if (dto.description !== undefined) perm.changeDescription(dto.description); + if (dto.code) perm.changeCode(dto.code); + + return this.permissionRepository.update(perm); + } +} diff --git a/src/modules/identity/application/handlers/role/assign-permission.handler.ts b/src/modules/identity/application/handlers/role/assign-permission.handler.ts new file mode 100644 index 0000000..95f892f --- /dev/null +++ b/src/modules/identity/application/handlers/role/assign-permission.handler.ts @@ -0,0 +1,23 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class RoleAssignPermissionHandler { + constructor( + private readonly roleRepository: IRole, + private readonly permissionRepository: IPermission, + ) {} + + async execute(roleId: string, permissionId: string) { + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + const perm = await this.permissionRepository.getById(permissionId); + if (!perm) throw new NotFoundException('Permission not found.'); + + role.assignPermission(perm); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/role/create-role.handler.ts b/src/modules/identity/application/handlers/role/create-role.handler.ts new file mode 100644 index 0000000..6cabff4 --- /dev/null +++ b/src/modules/identity/application/handlers/role/create-role.handler.ts @@ -0,0 +1,30 @@ +import { Injectable, ConflictException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { RoleData } from '../../../domain/entities/role.entity'; + +@Injectable() +export class CreateRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(dto: any) { + // check uniqueness by code + // simple check + try { + // attempt to create; repository may enforce uniqueness + const role = RoleData.restore({ + id: crypto.randomUUID(), + code: dto.code, + name: dto.name, + description: dto.description || '', + permissions: [], + isDefault: dto.isDefault || false, + createdAt: new Date(), + updatedAt: new Date(), + } as any); + + return this.roleRepository.create(role); + } catch (e) { + throw new ConflictException('Role creation failed.'); + } + } +} diff --git a/src/modules/identity/application/handlers/role/delete-role.handler.ts b/src/modules/identity/application/handlers/role/delete-role.handler.ts new file mode 100644 index 0000000..fa694ca --- /dev/null +++ b/src/modules/identity/application/handlers/role/delete-role.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class DeleteRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string) { + const role = await this.roleRepository.findById(id); + if (!role) throw new NotFoundException('Role not found.'); + + await this.roleRepository.delete(id); + } +} diff --git a/src/modules/identity/application/handlers/role/get-role.handler.ts b/src/modules/identity/application/handlers/role/get-role.handler.ts new file mode 100644 index 0000000..ea0df1c --- /dev/null +++ b/src/modules/identity/application/handlers/role/get-role.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class GetRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string) { + return this.roleRepository.findById(id); + } +} diff --git a/src/modules/identity/application/handlers/role/get-roles.handler.ts b/src/modules/identity/application/handlers/role/get-roles.handler.ts new file mode 100644 index 0000000..a931691 --- /dev/null +++ b/src/modules/identity/application/handlers/role/get-roles.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class GetRolesHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(query: { page?: number; limit?: number; search?: string }) { + return this.roleRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +} diff --git a/src/modules/identity/application/handlers/role/remove-permission.handler.ts b/src/modules/identity/application/handlers/role/remove-permission.handler.ts new file mode 100644 index 0000000..61a03c0 --- /dev/null +++ b/src/modules/identity/application/handlers/role/remove-permission.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class RoleRemovePermissionHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(roleId: string, permissionId: string) { + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + role.removePermission(permissionId); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/role/update-role.handler.ts b/src/modules/identity/application/handlers/role/update-role.handler.ts new file mode 100644 index 0000000..0b23bfc --- /dev/null +++ b/src/modules/identity/application/handlers/role/update-role.handler.ts @@ -0,0 +1,18 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class UpdateRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string, dto: any) { + const role = await this.roleRepository.findById(id); + if (!role) throw new NotFoundException('Role not found.'); + + if (dto.name) role.changeName(dto.name); + if (dto.description !== undefined) role.changeDescription(dto.description); + if (dto.isDefault !== undefined) role.setDefault(!!dto.isDefault); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/update-user.handler.ts b/src/modules/identity/application/handlers/update-user.handler.ts deleted file mode 100644 index d4e04d6..0000000 --- a/src/modules/identity/application/handlers/update-user.handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - Injectable, - ConflictException, - NotFoundException, -} from '@nestjs/common'; - -import { UpdateUserDto } from '../../presentation/dto/update-user.dto'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class UpdateUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute( - id: string, - dto: UpdateUserDto, - ): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - if ( - dto.email && - dto.email !== user.email - ) { - const exists = - await this.userRepository.existsByEmail(dto.email); - - if (exists) { - throw new ConflictException( - 'Email already exists.', - ); - } - - user.changeEmail(dto.email); - } - - if (dto.name) { - user.changeName(dto.name); - } - - await this.userRepository.update(user); - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/user/assign-permission.handler.ts b/src/modules/identity/application/handlers/user/assign-permission.handler.ts new file mode 100644 index 0000000..28b3b86 --- /dev/null +++ b/src/modules/identity/application/handlers/user/assign-permission.handler.ts @@ -0,0 +1,26 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { PermissionData } from '../../../domain/entities/permission.entity'; + +@Injectable() +export class AssignPermissionHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, permissionId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + // Permission repository is not available; create a minimal PermissionData + const perm = PermissionData.restore({ + id: permissionId, + name: permissionId, + description: '', + createdAt: new Date(), + updatedAt: new Date(), + }); + + user.assignPermission(perm); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/assign-role.handler.ts b/src/modules/identity/application/handlers/user/assign-role.handler.ts new file mode 100644 index 0000000..2b07179 --- /dev/null +++ b/src/modules/identity/application/handlers/user/assign-role.handler.ts @@ -0,0 +1,23 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class AssignRoleHandler { + constructor( + private readonly userRepository: IUser, + private readonly roleRepository: IRole, + ) {} + + async execute(userId: string, roleId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + user.assignRole(role); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/create-user.handler.ts b/src/modules/identity/application/handlers/user/create-user.handler.ts new file mode 100644 index 0000000..5bee566 --- /dev/null +++ b/src/modules/identity/application/handlers/user/create-user.handler.ts @@ -0,0 +1,31 @@ +import { + Injectable, + ConflictException, + BadRequestException, +} from '@nestjs/common'; + +import { CreateUserDto } from '../../../presentation/dto/create-user.dto'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { UserData } from '../../../domain/entities/user.entity'; +import { UserService } from '../../services/user.service'; + +@Injectable() +export class CreateUserHandler { + constructor( + private readonly userRepository: IUser, + private readonly userService: UserService, + ) {} + + async execute(dto: CreateUserDto): Promise { + + const exists = await this.userRepository.existByEmail(dto.email); + + if (exists) { + throw new ConflictException('Email already exists.'); + } + + // Provisioning disabled: Do not create users in external IdP from RayLab. + // Reject attempts to create users via API to enforce creation-at-Authentik policy. + throw new BadRequestException('User creation via RayLab API is disabled. Create users in Authentik and then login to sync.'); + } +} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/user/delete-user.handler.ts b/src/modules/identity/application/handlers/user/delete-user.handler.ts new file mode 100644 index 0000000..17c98a4 --- /dev/null +++ b/src/modules/identity/application/handlers/user/delete-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class DeleteUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + await this.userRepository.softDelete(id); + } +} diff --git a/src/modules/identity/application/handlers/user/disable-user.handler.ts b/src/modules/identity/application/handlers/user/disable-user.handler.ts new file mode 100644 index 0000000..87c627a --- /dev/null +++ b/src/modules/identity/application/handlers/user/disable-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class DisableUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.disable(id); + } +} diff --git a/src/modules/identity/application/handlers/user/enable-user.handler.ts b/src/modules/identity/application/handlers/user/enable-user.handler.ts new file mode 100644 index 0000000..dd73fbd --- /dev/null +++ b/src/modules/identity/application/handlers/user/enable-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class EnableUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.enable(id); + } +} diff --git a/src/modules/identity/application/handlers/user/get-current-user.handler.ts b/src/modules/identity/application/handlers/user/get-current-user.handler.ts new file mode 100644 index 0000000..a9e2184 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-current-user.handler.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { UserData } from '../../../domain/entities/user.entity'; + +@Injectable() +export class GetCurrentUserHandler { + async execute(currentUser: UserData) { + // currentUser is already domain user attached by CurrentUserGuard + return currentUser; + } +} diff --git a/src/modules/identity/application/handlers/user/get-user.handler.ts b/src/modules/identity/application/handlers/user/get-user.handler.ts new file mode 100644 index 0000000..d78d600 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-user.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class GetUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + return this.userRepository.getById(id); + } +} diff --git a/src/modules/identity/application/handlers/user/get-users.handler.ts b/src/modules/identity/application/handlers/user/get-users.handler.ts new file mode 100644 index 0000000..fe3af46 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-users.handler.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class GetUsersHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(query: { + page?: number; + limit?: number; + search?: string; + isActive?: boolean; + deleted?: boolean; + roleId?: string; + }) { + const res = await this.userRepository.find({ + page: query.page, + limit: query.limit, + search: query.search || null, + isActive: query.isActive !== undefined ? query.isActive : null, + deleted: query.deleted !== undefined ? query.deleted : null, + roleId: query.roleId || null, + }); + + return res; + } +} diff --git a/src/modules/identity/application/handlers/user/remove-permission.handler.ts b/src/modules/identity/application/handlers/user/remove-permission.handler.ts new file mode 100644 index 0000000..c8cb40d --- /dev/null +++ b/src/modules/identity/application/handlers/user/remove-permission.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RemovePermissionHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, permissionId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + user.removePermission(permissionId); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/remove-role.handler.ts b/src/modules/identity/application/handlers/user/remove-role.handler.ts new file mode 100644 index 0000000..b6c855a --- /dev/null +++ b/src/modules/identity/application/handlers/user/remove-role.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RemoveRoleHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, roleId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + user.removeRole(roleId); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/restore-user.handler.ts b/src/modules/identity/application/handlers/user/restore-user.handler.ts new file mode 100644 index 0000000..657ee47 --- /dev/null +++ b/src/modules/identity/application/handlers/user/restore-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RestoreUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.restore(id); + } +} diff --git a/src/modules/identity/application/handlers/user/sync-identity.handler.ts b/src/modules/identity/application/handlers/user/sync-identity.handler.ts new file mode 100644 index 0000000..aeb114b --- /dev/null +++ b/src/modules/identity/application/handlers/user/sync-identity.handler.ts @@ -0,0 +1,86 @@ +import { Injectable, UnauthorizedException, ForbiddenException, Inject } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { IAuthConfig } from '../../config/i-auth-config'; +import { UserData } from '../../../domain/entities/user.entity'; +import { IdentityData } from '../../../../../core/auth/interfaces/identity-data'; + +@Injectable() +export class SyncIdentityHandler { + constructor( + private readonly userRepository: IUser, + private readonly roleRepository: IRole, + private readonly authConfig: IAuthConfig, + ) {} + + async execute(identity: IdentityData): Promise { + const { sub, preferred_username, email } = identity as any; + + if (!sub) throw new UnauthorizedException('Invalid identity payload.'); + + // Try find by authentikId + let user = await this.userRepository.findByAuthentikId(sub); + + const syncEmail = this.authConfig.syncEmail(); + const syncUsername = this.authConfig.syncUsername(); + + if (!user) { + // User does not exist locally: create minimal local record per new architecture + const username = preferred_username || email || sub; + const userEntity = UserData.create({ username, email: email || '', authentikId: sub }); + + // assign default role if available + try { + const defaultRole = await this.roleRepository.getDefaultRole(); + if (defaultRole) { + userEntity.assignRole(defaultRole); + } + } catch (e) { + // ignore if role repo not available or no default role + } + + // persist local user + user = await this.userRepository.create(userEntity); + + // return freshly created user + return user; + } else { + let changed = false; + + if (syncEmail && email && user.email !== email) { + user.changeEmail(email); + changed = true; + + // prepare event class instance if needed (no dispatch) + } + + if (syncUsername && preferred_username && user.username !== preferred_username) { + user.changeUsername(preferred_username); + changed = true; + + // prepare event class instance if needed (no dispatch) + } + + // update last seen + user.touchLastSeen(); + changed = true; + + if (changed) { + user = await this.userRepository.update(user); + } + } + + // Validations + if (!user.isActive) { + // Authentication succeeded but user is disabled + throw new ForbiddenException('User is not active.'); + } + + if (user.deletedAt) { + throw new ForbiddenException('User is deleted.'); + } + + // Ensure roles/permissions loaded (repo should return includes) + return user; + } +} diff --git a/src/modules/identity/application/services/README.md b/src/modules/identity/application/services/README.md deleted file mode 100644 index 3ac5650..0000000 --- a/src/modules/identity/application/services/README.md +++ /dev/null @@ -1,12 +0,0 @@ -services/ - -Penjelasan: -Application services (use-cases) untuk module identity. - -Contoh file: -- get-user.service.ts -- create-user.service.ts - -Aturan: -- Application service mengorkestrasi domain services dan repository. -- Menangani transaction boundary jika perlu. diff --git a/src/modules/identity/application/services/user.service.ts b/src/modules/identity/application/services/user.service.ts new file mode 100644 index 0000000..bd921df --- /dev/null +++ b/src/modules/identity/application/services/user.service.ts @@ -0,0 +1,20 @@ +import { Injectable, BadRequestException, Logger } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IUser } from '../../domain/repositories/user.interface'; +import { UserData } from '../../domain/entities/user.entity'; + +@Injectable() +export class UserService { + private readonly logger = new Logger(UserService.name); + + constructor( + private readonly prisma: PrismaService, + private readonly userRepository: IUser, + ) {} + + async createUser(input: { username: string; email: string; roles?: string[]; isActive?: boolean }) { + // Provisioning to external Identity Provider (Authentik) has been disabled. + // All users must be created in Authentik first. RayLab will create local record on first successful login. + throw new BadRequestException('Provisioning disabled: create users in Authentik and login to sync to RayLab'); + } +} diff --git a/src/modules/identity/domain/entities/README.md b/src/modules/identity/domain/entities/README.md index bf9110b..b394cb0 100644 --- a/src/modules/identity/domain/entities/README.md +++ b/src/modules/identity/domain/entities/README.md @@ -1,11 +1 @@ -entities/ - -Penjelasan: -Entity domain untuk identity, mis. User, Profile. - -Contoh file: -- user.entity.ts -- profile.entity.ts - -Aturan: -- Entity berisi atribut dan mungkin method domain kecil (invariants), bukan orchestration. +ini adalah tempat class objek dibuat, objek user, role, dll. disini juga ada method dan harus diingat yang boleh mengubah objek ini hanya objek ini sendiri. \ No newline at end of file diff --git a/src/modules/identity/domain/entities/permission.entity.ts b/src/modules/identity/domain/entities/permission.entity.ts new file mode 100644 index 0000000..f27dce1 --- /dev/null +++ b/src/modules/identity/domain/entities/permission.entity.ts @@ -0,0 +1,40 @@ +export class PermissionData { + private constructor( + public readonly id: string, + public code: string, + public name: string, + public description: string, + public createdAt: Date, + public updatedAt: Date, + ) {} + + changeName(name: string) { + this.name = name; + } + + changeDescription(description: string) { + this.description = description; + } + + changeCode(code: string) { + this.code = code; + } + + static restore(props: { + id: string; + code?: string; + name: string; + description: string; + createdAt: Date; + updatedAt: Date; + }) { + return new PermissionData( + props.id, + props.code || props.name, + props.name, + props.description, + props.createdAt, + props.updatedAt, + ); + } +} \ No newline at end of file diff --git a/src/modules/identity/domain/entities/role.entity.ts b/src/modules/identity/domain/entities/role.entity.ts new file mode 100644 index 0000000..96a0d0f --- /dev/null +++ b/src/modules/identity/domain/entities/role.entity.ts @@ -0,0 +1,64 @@ +import { PermissionData } from "./permission.entity"; + +export class RoleData { + private constructor( + public readonly id: string, + public code: string, + public name: string, + public description: string, + public permissions: PermissionData[], + public isDefault: boolean, + public createdAt: Date, + public updatedAt: Date, + ) {} + + hasPermissions(permissionId: string): boolean { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); + } + + assignPermission(permissionData: PermissionData): void { + if (this.hasPermissions(permissionData.id)) throw new Error("Permission sudah dimiliki."); + + this.permissions.push(permissionData); + } + + removePermission(permissionId: string): void { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) throw new Error('Permission tidak ditemukan pada role.'); + this.permissions.splice(idx, 1); + } + + changeName(name: string) { + this.name = name; + } + + changeDescription(description: string) { + this.description = description; + } + + setDefault(isDefault: boolean) { + this.isDefault = isDefault; + } + + static restore(props: { + id: string; + code: string; + name: string; + description: string; + permissions?: PermissionData[]; + isDefault?: boolean; + createdAt: Date; + updatedAt: Date; + }) { + return new RoleData( + props.id, + props.code, + props.name, + props.description, + props.permissions || [], + props.isDefault ?? false, + props.createdAt, + props.updatedAt, + ); + } +} \ No newline at end of file diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index e1a4538..335462e 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -1,52 +1,196 @@ -export class User { - private constructor( +import { RoleData } from "./role.entity"; +import { PermissionData } from "./permission.entity"; + +export class UserData { + private constructor( public readonly id: string, - public name: string, + public authentikId: string | null, + public username: string, public email: string, - public password: string, - public metadata?: Record, + public password: string | null, + public roles: RoleData[], + public permissions: PermissionData[], + public isActive: boolean, + public deletedAt: Date | null, + public lastSeenAt: Date | null, + public storageQuota: number, + public storageUsed: number, ) {} - static create(data: { - name: string; + //#region Create + + static create(data: { + username: string; email: string; - password: string; - metadata?: Record; - }): User { - return new User( + password?: string | null; + authentikId?: string | null; + storageQuota?: number; + storageUsed?: number; + }): UserData { + const quota = data.storageQuota !== undefined ? data.storageQuota : 10737418240; // 10 GB + const used = data.storageUsed !== undefined ? data.storageUsed : 0; + return new UserData( crypto.randomUUID(), - data.name, + data.authentikId || null, + data.username, data.email, - data.password, - data.metadata, + data.password ?? null, + [], + [], + true, + null, + null, + quota, + used, ); } - delete() { - // Business Rule + //#endregion + + //#region Read + + hasRole(roleId: string) : boolean { + return this.roles.some(x => x.id.toLowerCase() === roleId.toLowerCase()) } - changeEmail(email: string) { + assignRole(roleData : RoleData) : void { + if (this.hasRole(roleData.id)) + throw new Error("Role Sudah dimiliki."); + + this.roles.push(roleData); + } + + hasPermissions(permissionId : string) : boolean { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()) + } + + hasPermission(permission: string) : boolean { + const byPerm = this.permissions.some(x => x.id.toLowerCase() === permission.toLowerCase() || x.name.toLowerCase() === permission.toLowerCase()); + if (byPerm) return true; + + // check roles + return this.roles.some(r => r.permissions.some(p => p.id.toLowerCase() === permission.toLowerCase() || p.name.toLowerCase() === permission.toLowerCase())); + } + + assignPermission(permissionData : PermissionData) : void { + if (this.hasPermissions(permissionData.id)) + throw new Error("Permission sudah dimiliki.") + + this.permissions.push(permissionData); + } + + //#endregion + + //#region Update + changeEmail(email: string) { this.email = email; } - changeName(name: string) { - this.name = name; + changeUsername(username: string) { + this.username = username; } - static restore(data: { - id: string; - name: string; - email: string; - password: string; - metadata?: Record; - }): User { - return new User( - data.id, - data.name, - data.email, - data.password, - data.metadata, - ); + touchLastSeen() { + this.lastSeenAt = new Date(); + } + + enable() { + this.isActive = true; + } + + disable() { + this.isActive = false; + } + //#endregion + + //#region Delete + softDelete() { + this.deletedAt = new Date(); + this.isActive = false; + } + + restoreInstance() { + this.deletedAt = null; + } + //#endregion + + public static restore(props: { + id: string; + authentikId?: string | null; + username: string; + email: string; + password: string | null; + roles?: RoleData[]; + permissions?: PermissionData[]; + isActive?: boolean; + deletedAt?: Date | null; + lastSeenAt?: Date | null; + storageQuota?: number | null; + storageUsed?: number | null; + }): UserData { + + return new UserData( + props.id, + props.authentikId || null, + props.username, + props.email, + props.password, + props.roles || [], + props.permissions || [], + props.isActive !== undefined ? props.isActive : true, + props.deletedAt || null, + props.lastSeenAt || null, + props.storageQuota !== undefined && props.storageQuota !== null ? props.storageQuota : 10737418240, + props.storageUsed !== undefined && props.storageUsed !== null ? props.storageUsed : 0, + ); + } + + removeRole(roleId: string) { + const idx = this.roles.findIndex(r => r.id.toLowerCase() === roleId.toLowerCase()); + if (idx === -1) throw new Error('Role tidak ditemukan pada user.'); + this.roles.splice(idx, 1); + } + + removePermission(permissionId: string) { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) throw new Error('Permission tidak ditemukan pada user.'); + this.permissions.splice(idx, 1); + } + + // Storage helpers + setStorageQuota(bytes: number) { + if (bytes < 0) throw new Error('storageQuota must be >= 0'); + if (this.storageUsed > bytes) throw new Error('storageQuota cannot be less than storageUsed'); + this.storageQuota = bytes; + } + + setStorageUsed(bytes: number) { + if (bytes < 0) throw new Error('storageUsed must be >= 0'); + if (bytes > this.storageQuota) throw new Error('storageUsed cannot exceed storageQuota'); + this.storageUsed = bytes; + } + + // Response helper for API + toResponse() { + const remaining = this.storageQuota - this.storageUsed; + const usagePercentage = this.storageQuota > 0 ? Math.round((this.storageUsed / this.storageQuota) * 100) : 0; + + return { + id: this.id, + authentikId: this.authentikId, + username: this.username, + email: this.email, + roles: this.roles, + permissions: this.permissions, + isActive: this.isActive, + deletedAt: this.deletedAt, + lastSeenAt: this.lastSeenAt, + storage: { + quota: this.storageQuota, + used: this.storageUsed, + remaining, + usagePercentage, + }, + }; } } \ No newline at end of file diff --git a/src/modules/identity/domain/events/user-created.event.ts b/src/modules/identity/domain/events/user-created.event.ts index 07fea7e..997431e 100644 --- a/src/modules/identity/domain/events/user-created.event.ts +++ b/src/modules/identity/domain/events/user-created.event.ts @@ -2,5 +2,6 @@ import { DomainEvent } from '../../../../core/events/event.interface'; export class UserCreatedEvent implements DomainEvent { readonly name = 'UserCreated'; - constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {} + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; authentikId: string; email: string; username: string }) {} } diff --git a/src/modules/identity/domain/events/user-deleted.event.ts b/src/modules/identity/domain/events/user-deleted.event.ts deleted file mode 100644 index 07cc1d4..0000000 --- a/src/modules/identity/domain/events/user-deleted.event.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DomainEvent } from '../../../../core/events/event.interface'; - -export class UserDeletedEvent implements DomainEvent { - readonly name = 'UserDeleted'; - constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {} -} diff --git a/src/modules/identity/domain/events/user-disabled.event.ts b/src/modules/identity/domain/events/user-disabled.event.ts new file mode 100644 index 0000000..2f243fc --- /dev/null +++ b/src/modules/identity/domain/events/user-disabled.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserDisabledEvent implements DomainEvent { + readonly name = 'UserDisabled'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string }) {} +} diff --git a/src/modules/identity/domain/events/user-email-changed.event.ts b/src/modules/identity/domain/events/user-email-changed.event.ts new file mode 100644 index 0000000..db50cbc --- /dev/null +++ b/src/modules/identity/domain/events/user-email-changed.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserEmailChangedEvent implements DomainEvent { + readonly name = 'UserEmailChanged'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; oldEmail: string; newEmail: string }) {} +} diff --git a/src/modules/identity/domain/events/user-enabled.event.ts b/src/modules/identity/domain/events/user-enabled.event.ts new file mode 100644 index 0000000..9ed1c60 --- /dev/null +++ b/src/modules/identity/domain/events/user-enabled.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserEnabledEvent implements DomainEvent { + readonly name = 'UserEnabled'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string }) {} +} diff --git a/src/modules/identity/domain/events/user-username-changed.event.ts b/src/modules/identity/domain/events/user-username-changed.event.ts new file mode 100644 index 0000000..7023331 --- /dev/null +++ b/src/modules/identity/domain/events/user-username-changed.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserUsernameChangedEvent implements DomainEvent { + readonly name = 'UserUsernameChanged'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; oldUsername: string; newUsername: string }) {} +} diff --git a/src/modules/identity/domain/repositories/README.MD b/src/modules/identity/domain/repositories/README.MD new file mode 100644 index 0000000..1d0ee81 --- /dev/null +++ b/src/modules/identity/domain/repositories/README.MD @@ -0,0 +1 @@ +ini adalah interface, semua yang akan dibuat oleh application/handler harus dibuat disini dulu. \ No newline at end of file diff --git a/src/modules/identity/domain/repositories/permission.interface.ts b/src/modules/identity/domain/repositories/permission.interface.ts new file mode 100644 index 0000000..a603ee2 --- /dev/null +++ b/src/modules/identity/domain/repositories/permission.interface.ts @@ -0,0 +1,9 @@ +import { PermissionData } from '../entities/permission.entity'; + +export abstract class IPermission { + abstract find(params: { page?: number; limit?: number; search?: string | null }): Promise<{ data: PermissionData[]; total: number }>; + abstract getById(id: string): Promise; + abstract create(permission: PermissionData): Promise; + abstract update(permission: PermissionData): Promise; + abstract delete(id: string): Promise; +} diff --git a/src/modules/identity/domain/repositories/role.interface.ts b/src/modules/identity/domain/repositories/role.interface.ts new file mode 100644 index 0000000..0405e8b --- /dev/null +++ b/src/modules/identity/domain/repositories/role.interface.ts @@ -0,0 +1,12 @@ +import { RoleData } from '../entities/role.entity'; + +export abstract class IRole { + abstract getDefaultRole(): Promise; + abstract findById(roleId: string): Promise; + + abstract find(params: { page?: number; limit?: number; search?: string | null }): Promise<{ data: RoleData[]; total: number }>; + + abstract create(role: RoleData): Promise; + abstract update(role: RoleData): Promise; + abstract delete(roleId: string): Promise; +} diff --git a/src/modules/identity/domain/repositories/user.interface.ts b/src/modules/identity/domain/repositories/user.interface.ts new file mode 100644 index 0000000..7a34862 --- /dev/null +++ b/src/modules/identity/domain/repositories/user.interface.ts @@ -0,0 +1,29 @@ +import { UserData } from '../entities/user.entity'; + +export abstract class IUser { + abstract create(user: UserData): Promise; + + abstract existsById(userId: string): Promise; + abstract existByEmail(userEmail: string): Promise; + abstract getById(userId: string): Promise; + abstract getByEmail(userEmail: string): Promise; + + abstract findByAuthentikId(authentikId: string): Promise; + + abstract update(user: UserData): Promise; + + abstract find(params: { + page?: number; + limit?: number; + search?: string | null; + isActive?: boolean | null; + deleted?: boolean | null; + roleId?: string | null; + }): Promise<{ data: UserData[]; total: number }>; + + abstract softDelete(userId: string): Promise; + abstract restore(userId: string): Promise; + + abstract enable(userId: string): Promise; + abstract disable(userId: string): Promise; +} \ No newline at end of file diff --git a/src/modules/identity/domain/repositories/user.repository.interface.ts b/src/modules/identity/domain/repositories/user.repository.interface.ts deleted file mode 100644 index f76c4a8..0000000 --- a/src/modules/identity/domain/repositories/user.repository.interface.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { User } from '../entities/user.entity'; - -export abstract class UserRepository { - abstract create(user: User): Promise; - - abstract update(user: User): Promise; - - abstract findById( - id: string, - ): Promise; - - abstract findAll(): Promise; - - abstract existsByEmail( - email: string, - ): Promise; -} \ No newline at end of file diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index 9074380..91ccfe3 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -1,52 +1,115 @@ import { Module } from '@nestjs/common'; import { UsersController } from './presentation/controllers/users.controller'; +import { RolesController } from './presentation/controllers/roles.controller'; +import { PermissionsController } from './presentation/controllers/permissions.controller'; import { PrismaService } from '../../shared/prisma.service'; +import { UserService } from './application/services/user.service'; import { PrismaUserRepository } from './infrastructure/repositories/prisma-user.repository'; -import { UserRepository } from './domain/repositories/user.repository.interface'; -import { EventDispatcher } from '../../core/events/event-dispatcher'; +import { IUser } from './domain/repositories/user.interface'; + + import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard'; -import { JwtModule } from '@nestjs/jwt'; -import { CreateUserHandler } from './application/handlers/create-user.handler'; -import { FindUserHandler } from './application/handlers/find-user.handler'; -import { DeleteUserHandler } from './application/handlers/delete-user.handler'; -import { FindUsersHandler } from './application/handlers/find-users.handler'; -import { UpdateUserHandler } from './application/handlers/update-user.handler'; +import { CreateUserHandler } from './application/handlers/user/create-user.handler'; import { PermissionGuard } from '../authorization/presentation/guards/permission.guard'; import { Reflector } from '@nestjs/core'; +import { SyncIdentityHandler } from './application/handlers/user/sync-identity.handler'; +import { CurrentUserGuard } from './presentation/guards/current-user.guard'; +import { PrismaRoleRepository } from './infrastructure/repositories/prisma-role.repository'; +import { PrismaPermissionRepository } from './infrastructure/repositories/prisma-permission.repository'; +import { IRole } from './domain/repositories/role.interface'; +import { IPermission } from './domain/repositories/permission.interface'; +import { IAuthConfig } from './application/config/i-auth-config'; +import { EnvAuthConfig } from './application/config/env-auth-config'; +import { GetUsersHandler } from './application/handlers/user/get-users.handler'; +import { GetUserHandler } from './application/handlers/user/get-user.handler'; +import { GetCurrentUserHandler } from './application/handlers/user/get-current-user.handler'; +import { EnableUserHandler } from './application/handlers/user/enable-user.handler'; +import { DisableUserHandler } from './application/handlers/user/disable-user.handler'; +import { DeleteUserHandler } from './application/handlers/user/delete-user.handler'; +import { RestoreUserHandler } from './application/handlers/user/restore-user.handler'; +import { AssignRoleHandler } from './application/handlers/user/assign-role.handler'; +import { RemoveRoleHandler } from './application/handlers/user/remove-role.handler'; +import { AssignPermissionHandler } from './application/handlers/user/assign-permission.handler'; +import { RemovePermissionHandler } from './application/handlers/user/remove-permission.handler'; + +import { GetRolesHandler } from './application/handlers/role/get-roles.handler'; +import { GetRoleHandler } from './application/handlers/role/get-role.handler'; +import { CreateRoleHandler } from './application/handlers/role/create-role.handler'; +import { UpdateRoleHandler } from './application/handlers/role/update-role.handler'; +import { DeleteRoleHandler } from './application/handlers/role/delete-role.handler'; +import { RoleAssignPermissionHandler } from './application/handlers/role/assign-permission.handler'; +import { RoleRemovePermissionHandler } from './application/handlers/role/remove-permission.handler'; + +import { GetPermissionsHandler } from './application/handlers/permission/get-permissions.handler'; +import { GetPermissionHandler } from './application/handlers/permission/get-permission.handler'; +import { CreatePermissionHandler } from './application/handlers/permission/create-permission.handler'; +import { UpdatePermissionHandler } from './application/handlers/permission/update-permission.handler'; +import { DeletePermissionHandler } from './application/handlers/permission/delete-permission.handler'; @Module({ - imports: [ - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { - expiresIn: '1d', - }, - }), - ], - controllers: [UsersController], providers: [ - //#region User - CreateUserHandler, - FindUserHandler, - FindUsersHandler, - UpdateUserHandler, + //#region User + // CreateUserHandler has been removed: provisioning disabled; users must be created in Authentik. + SyncIdentityHandler, + GetUsersHandler, + + GetUserHandler, + GetCurrentUserHandler, + EnableUserHandler, + DisableUserHandler, DeleteUserHandler, + RestoreUserHandler, + AssignRoleHandler, + RemoveRoleHandler, + AssignPermissionHandler, + RemovePermissionHandler, //#endregion + // role & permission handlers + GetRolesHandler, + GetRoleHandler, + CreateRoleHandler, + UpdateRoleHandler, + DeleteRoleHandler, + RoleAssignPermissionHandler, + RoleRemovePermissionHandler, + + GetPermissionsHandler, + GetPermissionHandler, + CreatePermissionHandler, + UpdatePermissionHandler, + DeletePermissionHandler, + JwtAuthGuard, + CurrentUserGuard, PermissionGuard, Reflector, - PrismaService, + PrismaService, PrismaUserRepository, + PrismaRoleRepository, + PrismaPermissionRepository, + UserService, { - provide: UserRepository, + provide: IUser, useClass: PrismaUserRepository, }, - { - provide: 'EVENT_DISPATCHER', - useValue: new EventDispatcher(), + { + provide: IRole, + useClass: PrismaRoleRepository, }, - ], + { + provide: IPermission, + useClass: PrismaPermissionRepository, + }, + { + provide: IAuthConfig, + useClass: EnvAuthConfig, + }, + ], + imports: [], + controllers: [UsersController, RolesController, PermissionsController], }) export class IdentityModule {} + + diff --git a/src/modules/identity/infrastructure/README.md b/src/modules/identity/infrastructure/README.md index 06bd408..006593d 100644 --- a/src/modules/identity/infrastructure/README.md +++ b/src/modules/identity/infrastructure/README.md @@ -1,12 +1 @@ -modules/identity/infrastructure/ - -Penjelasan: -Implementasi teknis untuk module identity, seperti Prisma repository, adapter implementations, dan data mappers. - -Contoh file: -- prisma/user.repository.ts (mengimplementasikan domain repository interface) -- adapter/identity-adapter.ts - -Aturan: -- Infrastruktur hanya mengimplementasikan interface domain; jangan memuat business rules. -- Import dari infrastructure ke domain harus satu arah: infrastructure -> domain (implementasi). +infrastrucute adalah tempat yang langsung berhubungan dengan dunia luar, misal db, server lain, JWT, dsb. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/README.MD b/src/modules/identity/infrastructure/mappers/README.MD new file mode 100644 index 0000000..7a42625 --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/README.MD @@ -0,0 +1 @@ +disini emngubah dari domain/entity menjadi format di database dan sebaliknya. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts new file mode 100644 index 0000000..05abad0 --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts @@ -0,0 +1,23 @@ +import { PermissionData } from '../../domain/entities/permission.entity'; + +export class PrismaPermissionMapper { + static toDomain(model: any): PermissionData { + return PermissionData.restore({ + id: model.id, + code: model.code || model.name, + name: model.name, + description: model.description, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } + + static toPersistence(permission: PermissionData) { + return { + id: permission.id, + code: permission.code, + name: permission.name, + description: permission.description, + }; + } +} diff --git a/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts new file mode 100644 index 0000000..133431d --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts @@ -0,0 +1,27 @@ +import { RoleData } from '../../domain/entities/role.entity'; +import { PermissionData } from '../../domain/entities/permission.entity'; + +export class PrismaRoleMapper { + static toDomain(model: any): RoleData { + const permissions: PermissionData[] = (model.permissions || []).map((p: any) => + PermissionData.restore({ + id: p.id, + name: p.name, + description: p.description, + createdAt: p.createdAt, + updatedAt: p.updatedAt, + }), + ); + + return RoleData.restore({ + id: model.id, + code: model.code, + name: model.name, + description: model.description, + permissions, + isDefault: model.isDefault ?? false, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } +} \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts index 1ce360d..2a8dcad 100644 --- a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts +++ b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts @@ -1,27 +1,73 @@ -import { User } from '../../domain/entities/user.entity'; +import { UserData } from '../../domain/entities/user.entity'; +import { RoleData } from '../../domain/entities/role.entity'; +import { PermissionData } from '../../domain/entities/permission.entity'; export class PrismaUserMapper { - static toDomain(model: any): User | null { - if (!model) { - return null; - } + static toDomain(model: any): UserData { + const roles: RoleData[] = (model.roles || []).map((ur: any) => { + const r = ur.role; + const permissions: PermissionData[] = (r?.permissions || []).map((rp: any) => + PermissionData.restore({ + id: rp.permission.id, + name: rp.permission.name, + description: rp.permission.description, + createdAt: rp.permission.createdAt, + updatedAt: rp.permission.updatedAt, + }), + ); - return User.restore({ + return RoleData.restore({ + id: r.id, + code: r.code, + name: r.name, + description: r.description, + permissions, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + }); + }); + + const permissions: PermissionData[] = (model.permissions || []).map((up: any) => + PermissionData.restore({ + id: up.permission.id, + name: up.permission.name, + description: up.permission.description, + createdAt: up.permission.createdAt, + updatedAt: up.permission.updatedAt, + }), + ); + + return UserData.restore({ id: model.id, - name: model.name, + authentikId: model.authentikUserId || model.authentikId || null, + username: model.username || model.name || model.username, email: model.email, - password: model.password, - metadata: model.metadata, + password: model.password ?? null, + roles, + permissions, + isActive: model.isActive ?? true, + deletedAt: model.deletedAt || null, + lastSeenAt: model.lastSeenAt || null, + storageQuota: model.storageQuota !== undefined && model.storageQuota !== null ? Number(model.storageQuota) : undefined, + storageUsed: model.storageUsed !== undefined && model.storageUsed !== null ? Number(model.storageUsed) : undefined, }); } - static toPersistence(user: User) { - return { - id: user.id, - name: user.name, - email: user.email, - password: user.password, - metadata: user.metadata ?? {}, - }; + static toPersistence(userData: UserData) { + return { + id: userData.id, + authentikUserId: (userData as any).authentikUserId || userData.authentikId, + authentikId: userData.authentikId, + username: userData.username, + email: userData.email, + password: userData.password ?? null, + isActive: userData.isActive, + deletedAt: userData.deletedAt, + lastSeenAt: userData.lastSeenAt, + lastSyncedAt: (userData as any).lastSyncedAt, + syncStatus: (userData as any).syncStatus, + storageQuota: (userData as any).storageQuota, + storageUsed: (userData as any).storageUsed, + }; } } \ No newline at end of file diff --git a/src/modules/identity/infrastructure/persistence/README.md b/src/modules/identity/infrastructure/persistence/README.md new file mode 100644 index 0000000..07d7361 --- /dev/null +++ b/src/modules/identity/infrastructure/persistence/README.md @@ -0,0 +1 @@ +ini adalah tempat konfigurasi database. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/repositories/README.md b/src/modules/identity/infrastructure/repositories/README.md index b7e780b..455ca20 100644 --- a/src/modules/identity/infrastructure/repositories/README.md +++ b/src/modules/identity/infrastructure/repositories/README.md @@ -1,11 +1 @@ -repositories/ - -Penjelasan: -Implementasi repository di layer infrastructure. Biasanya berisi Prisma queries dan mapping antara DB model dan domain entity. - -Contoh file: -- prisma/user.repository.ts - -Aturan: -- Repository mengimplementasikan interface di domain layer. -- Hindari business logic di repository. +disini yang menjalankan logika bisnisnya. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts new file mode 100644 index 0000000..9459cff --- /dev/null +++ b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IPermission } from '../../domain/repositories/permission.interface'; +import { PrismaPermissionMapper } from '../mappers/prisma-permission.mapper'; +import { PermissionData } from '../../domain/entities/permission.entity'; + +@Injectable() +export class PrismaPermissionRepository implements IPermission { + constructor(private readonly prisma: PrismaService) {} + + async find(params: { page?: number; limit?: number; search?: string | null }) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + const [total, items] = await Promise.all([ + this.prisma.permission.count({ where }), + this.prisma.permission.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' } }), + ]); + + return { data: items.map(i => PrismaPermissionMapper.toDomain(i)!), total }; + } + + async getById(id: string) { + const p = await this.prisma.permission.findUnique({ where: { id } }); + if (!p) throw new Error('Permission not found.'); + return PrismaPermissionMapper.toDomain(p); + } + + async create(permission: PermissionData) { + const base = PrismaPermissionMapper.toPersistence(permission); + const created = await this.prisma.permission.create({ data: base }); + return PrismaPermissionMapper.toDomain(created)!; + } + + async update(permission: PermissionData) { + const base = PrismaPermissionMapper.toPersistence(permission); + const updated = await this.prisma.permission.update({ where: { id: permission.id }, data: base }); + return PrismaPermissionMapper.toDomain(updated)!; + } + + async delete(id: string) { + await this.prisma.permission.delete({ where: { id } }); + } +} diff --git a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts new file mode 100644 index 0000000..e2bbbee --- /dev/null +++ b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts @@ -0,0 +1,109 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IRole } from '../../domain/repositories/role.interface'; +import { PrismaRoleMapper } from '../mappers/prisma-role.mapper'; +import { RoleData } from '../../domain/entities/role.entity'; + +@Injectable() +export class PrismaRoleRepository implements IRole { + constructor(private readonly prisma: PrismaService) {} + + async getDefaultRole() { + const role = await this.prisma.role.findFirst({ + where: { isDefault: true }, + include: { permissions: true }, + }); + + if (!role) return null; + + return PrismaRoleMapper.toDomain(role); + } + + async findById(roleId: string) { + const role = await this.prisma.role.findUnique({ + where: { id: roleId }, + include: { permissions: true }, + }); + + if (!role) throw new Error('Role not found.'); + + return PrismaRoleMapper.toDomain(role); + } + + async find(params: { page?: number; limit?: number; search?: string | null }) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + const [total, items] = await Promise.all([ + this.prisma.role.count({ where }), + this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: true } }), + ]); + + return { data: items.map(i => PrismaRoleMapper.toDomain(i)!), total }; + } + + async create(role: RoleData) { + const base: any = { + id: role.id, + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + + if (role.permissions && role.permissions.length > 0) { + base.permissions = { + create: role.permissions.map(p => ({ permission: { connect: { id: p.id } }, assignedBy: 'system' })), + }; + } + + const created = await this.prisma.role.create({ data: base, include: { permissions: true } }); + return PrismaRoleMapper.toDomain(created)!; + } + + async update(role: RoleData) { + const base: any = { + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + + // sync permissions via transaction + const permIds = role.permissions ? role.permissions.map(p => p.id) : []; + + await this.prisma.$transaction(async (prisma) => { + const currentPerms = await prisma.rolePermission.findMany({ where: { roleId: role.id } }); + const currentIds = currentPerms.map(p => p.permissionId); + + const toAdd = permIds.filter(id => !currentIds.includes(id)); + const toRemove = currentIds.filter(id => !permIds.includes(id)); + + if (toRemove.length > 0) { + await prisma.rolePermission.deleteMany({ where: { roleId: role.id, permissionId: { in: toRemove } } }); + } + + if (toAdd.length > 0) { + await prisma.rolePermission.createMany({ data: toAdd.map(pid => ({ roleId: role.id, permissionId: pid, assignedBy: 'system' })) as any, skipDuplicates: true }); + } + + await prisma.role.update({ where: { id: role.id }, data: base }); + }); + + const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: true } }); + return PrismaRoleMapper.toDomain(updated)!; + } + + async delete(roleId: string) { + await this.prisma.role.delete({ where: { id: roleId } }); + } +} diff --git a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts index 74bc603..e575bc2 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts @@ -1,64 +1,257 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../../../../shared/prisma.service'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; +import { IUser } from '../../domain/repositories/user.interface'; +import { UserData } from '../../domain/entities/user.entity'; import { PrismaUserMapper } from '../mappers/prisma-user.mapper'; @Injectable() -export class PrismaUserRepository implements UserRepository { +export class PrismaUserRepository implements IUser { constructor(private readonly prisma: PrismaService) {} + + //#region Create - async findById(id: string) { - const user = await this.prisma.user.findUnique({ - where: { id }, - }); + async create(userData: UserData): Promise { + const base = PrismaUserMapper.toPersistence(userData); - return PrismaUserMapper.toDomain(user); - } + const data: any = { ...base }; - async findAll(): Promise { - const users = await this.prisma.user.findMany({ - where: { deleted_at: null }, - orderBy: { created_at: 'desc' }, - }); + if (userData.roles && userData.roles.length > 0) { + data.roles = { + create: userData.roles.map(r => ({ role: { connect: { id: r.id } }, assignedBy: 'system' })), + }; + } - return users.map(PrismaUserMapper.toDomain); - } - - async create(user: User): Promise { const created = await this.prisma.user.create({ - data: PrismaUserMapper.toPersistence(user), + data, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, }); return PrismaUserMapper.toDomain(created)!; } - async update(user: User): Promise { + //#endregion + + //#region Read + + async existsById(userId: string): Promise { + return ( + (await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + })) !== null + ); + } + + async existByEmail(userEmail: string): Promise { + return ( + (await this.prisma.user.findUnique({ + where: { email: userEmail }, + select: { email: true }, + })) !== null + ); + } + + async getById(userId: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { id: userId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (userData === null) throw new Error('User tidak ditemukan.'); + + return PrismaUserMapper.toDomain(userData); + } + + async getByEmail(userEmail: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { email: userEmail }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (userData === null) throw new Error('User tidak ditemukan.'); + + return PrismaUserMapper.toDomain(userData); + } + + async findByAuthentikId(authentikId: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { authentikId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (!userData) return null; + + return PrismaUserMapper.toDomain(userData); + } + + async find(params: { + page?: number; + limit?: number; + search?: string | null; + isActive?: boolean | null; + deleted?: boolean | null; + roleId?: string | null; + }): Promise<{ data: UserData[]; total: number }> { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + + if (params.search) { + where.OR = [ + { username: { contains: params.search, mode: 'insensitive' } }, + { email: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + if (params.isActive !== undefined && params.isActive !== null) { + where.isActive = params.isActive; + } + + if (params.deleted !== undefined && params.deleted !== null) { + if (params.deleted) where.deletedAt = { not: null }; + else where.deletedAt = null; + } + + if (params.roleId) { + where.roles = { some: { roleId: params.roleId } }; + } + + const [total, items] = await Promise.all([ + this.prisma.user.count({ where }), + this.prisma.user.findMany({ + where, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + skip: (page - 1) * limit, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + ]); + + return { data: items.map(i => PrismaUserMapper.toDomain(i)!), total }; + } + + async softDelete(userId: string): Promise { + await this.prisma.user.update({ where: { id: userId }, data: { deletedAt: new Date(), isActive: false } }); + } + + async restore(userId: string): Promise { const updated = await this.prisma.user.update({ - where: { - id: user.id, + where: { id: userId }, + data: { deletedAt: null }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, }, - data: PrismaUserMapper.toPersistence(user), }); return PrismaUserMapper.toDomain(updated)!; } - async delete(id: string) { - const user = await this.prisma.user.update({ - where: { id }, - data: { deleted_at: new Date() }, + async enable(userId: string): Promise { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: true }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, }); - return PrismaUserMapper.toDomain(user); + + return PrismaUserMapper.toDomain(updated)!; } - async existsByEmail(email: string, excludeId?: string | null) { - const where: any = { email }; - if (excludeId) { - where.id = { not: excludeId }; - } + async disable(userId: string): Promise { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: false }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); - const user = await this.prisma.user.findFirst({ where }); - return !!user; + return PrismaUserMapper.toDomain(updated)!; } + + async update(userData: UserData): Promise { + const base = PrismaUserMapper.toPersistence(userData); + + const data: any = { ...base }; + + // sync roles and permissions using transaction + const roleIds = userData.roles ? userData.roles.map(r => r.id) : []; + const permissionIds = userData.permissions ? userData.permissions.map(p => p.id) : []; + + // perform transaction: delete removed relations, create missing ones, update user + await this.prisma.$transaction(async (prisma) => { + // current roles + const currentRoles = await prisma.userRole.findMany({ where: { userId: userData.id } }); + const currentRoleIds = currentRoles.map(r => r.roleId); + + const toAddRoles = roleIds.filter(id => !currentRoleIds.includes(id)); + const toRemoveRoles = currentRoleIds.filter(id => !roleIds.includes(id)); + + if (toRemoveRoles.length > 0) { + await prisma.userRole.deleteMany({ where: { userId: userData.id, roleId: { in: toRemoveRoles } } }); + } + + if (toAddRoles.length > 0) { + await prisma.userRole.createMany({ + data: toAddRoles.map(rid => ({ userId: userData.id, roleId: rid, assignedBy: 'system' })) as any, + skipDuplicates: true, + }); + } + + // permissions + const currentPerms = await prisma.userPermission.findMany({ where: { userId: userData.id } }); + const currentPermIds = currentPerms.map(p => p.permissionId); + + const toAddPerms = permissionIds.filter(id => !currentPermIds.includes(id)); + const toRemovePerms = currentPermIds.filter(id => !permissionIds.includes(id)); + + if (toRemovePerms.length > 0) { + await prisma.userPermission.deleteMany({ where: { userId: userData.id, permissionId: { in: toRemovePerms } } }); + } + + if (toAddPerms.length > 0) { + await prisma.userPermission.createMany({ + data: toAddPerms.map(pid => ({ userId: userData.id, permissionId: pid, assignedBy: 'system' })) as any, + skipDuplicates: true, + }); + } + + // update base user fields + await prisma.user.update({ where: { id: userData.id }, data }); + }); + + const updated = await this.prisma.user.findUnique({ + where: { id: userData.id }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + return PrismaUserMapper.toDomain(updated)!; + } + + //#endregion } + + diff --git a/src/modules/identity/presentation/controllers/permissions.controller.ts b/src/modules/identity/presentation/controllers/permissions.controller.ts new file mode 100644 index 0000000..73e3fe9 --- /dev/null +++ b/src/modules/identity/presentation/controllers/permissions.controller.ts @@ -0,0 +1,67 @@ +import { Controller, Get, Post, Patch, Delete, Param, UseGuards, Body, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; +import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; +import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; + +import { GetPermissionsHandler } from '../../application/handlers/permission/get-permissions.handler'; +import { GetPermissionHandler } from '../../application/handlers/permission/get-permission.handler'; +import { CreatePermissionHandler } from '../../application/handlers/permission/create-permission.handler'; +import { UpdatePermissionHandler } from '../../application/handlers/permission/update-permission.handler'; +import { DeletePermissionHandler } from '../../application/handlers/permission/delete-permission.handler'; + +@ApiTags('Permissions') +@Controller('permissions') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) +export class PermissionsController { + constructor( + private readonly getPermissionsHandler: GetPermissionsHandler, + private readonly getPermissionHandler: GetPermissionHandler, + private readonly createPermissionHandler: CreatePermissionHandler, + private readonly updatePermissionHandler: UpdatePermissionHandler, + private readonly deletePermissionHandler: DeletePermissionHandler, + ) {} + + @Get() + @Permissions(PermissionType.PERMISSION_READ) + @ApiOperation({ summary: 'List permissions' }) + async findAll(@Query() query: any) { + const res = await this.getPermissionsHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + + @Get(':id') + @Permissions(PermissionType.PERMISSION_READ) + @ApiOperation({ summary: 'Get permission' }) + async findOne(@Param('id') id: string) { + const p = await this.getPermissionHandler.execute(id); + return { success: true, data: p, meta: {} }; + } + + @Post() + @Permissions(PermissionType.PERMISSION_CREATE) + @ApiOperation({ summary: 'Create permission' }) + async create(@Body() body: any) { + const created = await this.createPermissionHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + + @Patch(':id') + @Permissions(PermissionType.PERMISSION_UPDATE) + @ApiOperation({ summary: 'Update permission' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updatePermissionHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id') + @Permissions(PermissionType.PERMISSION_DELETE) + @ApiOperation({ summary: 'Delete permission' }) + async remove(@Param('id') id: string) { + await this.deletePermissionHandler.execute(id); + return { success: true, data: null, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/controllers/roles.controller.ts b/src/modules/identity/presentation/controllers/roles.controller.ts new file mode 100644 index 0000000..6a8d23e --- /dev/null +++ b/src/modules/identity/presentation/controllers/roles.controller.ts @@ -0,0 +1,88 @@ +import { Controller, Get, Post, Patch, Delete, Param, UseGuards, Body, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; +import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; +import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; + +import { GetRolesHandler } from '../../application/handlers/role/get-roles.handler'; +import { GetRoleHandler } from '../../application/handlers/role/get-role.handler'; +import { CreateRoleHandler } from '../../application/handlers/role/create-role.handler'; +import { UpdateRoleHandler } from '../../application/handlers/role/update-role.handler'; +import { DeleteRoleHandler } from '../../application/handlers/role/delete-role.handler'; +import { RoleAssignPermissionHandler } from '../../application/handlers/role/assign-permission.handler'; +import { RoleRemovePermissionHandler } from '../../application/handlers/role/remove-permission.handler'; + +@ApiTags('Roles') +@Controller('roles') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) +export class RolesController { + constructor( + private readonly getRolesHandler: GetRolesHandler, + private readonly getRoleHandler: GetRoleHandler, + private readonly createRoleHandler: CreateRoleHandler, + private readonly updateRoleHandler: UpdateRoleHandler, + private readonly deleteRoleHandler: DeleteRoleHandler, + private readonly roleAssignPermissionHandler: RoleAssignPermissionHandler, + private readonly roleRemovePermissionHandler: RoleRemovePermissionHandler, + ) {} + + @Get() + @Permissions(PermissionType.ROLE_READ) + @ApiOperation({ summary: 'List roles' }) + async findAll(@Query() query: any) { + const res = await this.getRolesHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + + @Get(':id') + @Permissions(PermissionType.ROLE_READ) + @ApiOperation({ summary: 'Get role' }) + async findOne(@Param('id') id: string) { + const role = await this.getRoleHandler.execute(id); + return { success: true, data: role, meta: {} }; + } + + @Post() + @Permissions(PermissionType.ROLE_CREATE) + @ApiOperation({ summary: 'Create role' }) + async create(@Body() body: any) { + const created = await this.createRoleHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + + @Patch(':id') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Update role' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updateRoleHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id') + @Permissions(PermissionType.ROLE_DELETE) + @ApiOperation({ summary: 'Delete role' }) + async remove(@Param('id') id: string) { + await this.deleteRoleHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + + @Post(':id/permissions') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Assign permission to role' }) + async assignPermission(@Param('id') id: string, @Body() body: any) { + const permissionId = body.permissionId; + const updated = await this.roleAssignPermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id/permissions/:permissionId') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Remove permission from role' }) + async removePermission(@Param('id') id: string, @Param('permissionId') permissionId: string) { + const updated = await this.roleRemovePermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/controllers/users.controller.ts b/src/modules/identity/presentation/controllers/users.controller.ts index edd1057..73e766f 100644 --- a/src/modules/identity/presentation/controllers/users.controller.ts +++ b/src/modules/identity/presentation/controllers/users.controller.ts @@ -1,101 +1,134 @@ -import { - Controller, - Get, - Post, - Delete, - Param, - UseGuards, - Body, - Patch -} from '@nestjs/common'; +import { Controller, Get, Param, UseGuards, Query, Patch, Delete, Post, Body, Req } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; -import { FindUserHandler } from '../../application/handlers/find-user.handler'; -import { FindUsersHandler } from '../../application/handlers/find-users.handler'; -import { CreateUserHandler } from '../../application/handlers/create-user.handler'; -import { UpdateUserHandler } from '../../application/handlers/update-user.handler'; -import { DeleteUserHandler } from '../../application/handlers/delete-user.handler'; -import { CreateUserDto } from '../dto/create-user.dto'; -import { UpdateUserDto } from '../dto/update-user.dto'; +import { GetUsersHandler } from '../../application/handlers/user/get-users.handler'; +import { GetUserHandler } from '../../application/handlers/user/get-user.handler'; +import { GetCurrentUserHandler } from '../../application/handlers/user/get-current-user.handler'; +import { EnableUserHandler } from '../../application/handlers/user/enable-user.handler'; +import { DisableUserHandler } from '../../application/handlers/user/disable-user.handler'; +import { DeleteUserHandler } from '../../application/handlers/user/delete-user.handler'; +import { RestoreUserHandler } from '../../application/handlers/user/restore-user.handler'; +import { AssignRoleHandler } from '../../application/handlers/user/assign-role.handler'; +import { RemoveRoleHandler } from '../../application/handlers/user/remove-role.handler'; +import { AssignPermissionHandler } from '../../application/handlers/user/assign-permission.handler'; +import { RemovePermissionHandler } from '../../application/handlers/user/remove-permission.handler'; @ApiTags('Users') -@Controller('api/v1/users') -@UseGuards(JwtAuthGuard, PermissionGuard) +@Controller('users') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) export class UsersController { constructor( - private readonly createUserHandler: CreateUserHandler, - private readonly findUserHandler: FindUserHandler, - private readonly findUsersHandler: FindUsersHandler, - private readonly updateUserHandler: UpdateUserHandler, + private readonly getUsersHandler: GetUsersHandler, + private readonly getUserHandler: GetUserHandler, + private readonly getCurrentUserHandler: GetCurrentUserHandler, + private readonly enableUserHandler: EnableUserHandler, + private readonly disableUserHandler: DisableUserHandler, private readonly deleteUserHandler: DeleteUserHandler, + private readonly restoreUserHandler: RestoreUserHandler, + private readonly assignRoleHandler: AssignRoleHandler, + private readonly removeRoleHandler: RemoveRoleHandler, + private readonly assignPermissionHandler: AssignPermissionHandler, + private readonly removePermissionHandler: RemovePermissionHandler, ) {} - @Post() - @Permissions('USER_CREATE') - @ApiOperation({ summary: 'Create user' }) - async create(@Body() dto: CreateUserDto) { - const data = await this.createUserHandler.execute(dto); + @Get() + @Permissions(PermissionType.USER_READ) + @ApiOperation({ summary: 'List users' }) + async findAll(@Query() query: any) { + const res = await this.getUsersHandler.execute(query); return { success: true, - data, - meta: {}, - } + data: res.data.map(u => (u as any).toResponse ? (u as any).toResponse() : u), + meta: { total: res.total }, + }; + } + + @Get('me') + @ApiOperation({ summary: 'Get current user' }) + async me(@Req() req: any) { + const user = await this.getCurrentUserHandler.execute(req.currentUser); + + return { success: true, data: (user as any).toResponse ? (user as any).toResponse() : user, meta: {} }; } @Get(':id') - @Permissions('USER_READ') + @Permissions(PermissionType.USER_READ) @ApiOperation({ summary: 'Get user by id' }) - async getById(@Param('id') id: string) { - const data = await this.findUserHandler.execute(id); - - return { - success: true, - data, - meta: {}, - }; + async findOne(@Param('id') id: string) { + const user = await this.getUserHandler.execute(id); + return { success: true, data: (user as any).toResponse ? (user as any).toResponse() : user, meta: {} }; } - @Get() - @Permissions('USER_READ_ADMIN') - @ApiOperation({ summary: 'Get All User' }) - async getAll() { - const data = await this.findUsersHandler.execute(); - - return { - success: true, - data, - meta: {}, - }; + @Patch(':id/enable') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Enable user' }) + async enable(@Param('id') id: string) { + const updated = await this.enableUserHandler.execute(id); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; } - @Patch(':id') - @Permissions('USER_UPDATE') - @ApiOperation({ summary: 'Update User' }) - async update(@Param('id') id:string, @Body() dto:UpdateUserDto) { - const data = await this.updateUserHandler.execute(id, dto); - - return { - success: true, - data, - meta: {}, - } + @Patch(':id/disable') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Disable user' }) + async disable(@Param('id') id: string) { + const updated = await this.disableUserHandler.execute(id); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; } @Delete(':id') - @Permissions('USER_DELETE') - @ApiOperation({ summary: 'Delete user by id' }) - async delete(@Param('id') id: string) { + @Permissions(PermissionType.USER_DELETE) + @ApiOperation({ summary: 'Soft delete user' }) + async remove(@Param('id') id: string) { await this.deleteUserHandler.execute(id); - - return { - success: true, - data: null, - meta: {}, - }; + return { success: true, data: null, meta: {} }; } -} \ No newline at end of file + + @Post(':id/restore') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Restore user' }) + async restore(@Param('id') id: string) { + const restored = await this.restoreUserHandler.execute(id); + return { success: true, data: (restored as any).toResponse ? (restored as any).toResponse() : restored, meta: {} }; + } + + @Post(':id/roles') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Assign role to user' }) + async assignRole(@Param('id') id: string, @Body() body: any) { + const roleId = body.roleId; + const updated = await this.assignRoleHandler.execute(id, roleId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Delete(':id/roles/:roleId') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Remove role from user' }) + async removeRole(@Param('id') id: string, @Param('roleId') roleId: string) { + const updated = await this.removeRoleHandler.execute(id, roleId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Post(':id/permissions') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Assign permission to user' }) + async assignPermission(@Param('id') id: string, @Body() body: any) { + const permissionId = body.permissionId; + const updated = await this.assignPermissionHandler.execute(id, permissionId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Delete(':id/permissions/:permissionId') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Remove permission from user' }) + async removePermission(@Param('id') id: string, @Param('permissionId') permissionId: string) { + const updated = await this.removePermissionHandler.execute(id, permissionId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/dto/create-permission.dto.ts b/src/modules/identity/presentation/dto/create-permission.dto.ts new file mode 100644 index 0000000..c030a48 --- /dev/null +++ b/src/modules/identity/presentation/dto/create-permission.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreatePermissionDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + code!: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + name!: string; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + description?: string; +} diff --git a/src/modules/identity/presentation/dto/create-role.dto.ts b/src/modules/identity/presentation/dto/create-role.dto.ts new file mode 100644 index 0000000..6104e72 --- /dev/null +++ b/src/modules/identity/presentation/dto/create-role.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateRoleDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + code!: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + name!: string; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + description?: string; + + @ApiProperty({ required: false }) + @IsOptional() + isDefault?: boolean; +} diff --git a/src/modules/identity/presentation/dto/create-user.dto.ts b/src/modules/identity/presentation/dto/create-user.dto.ts index adcfdea..ee203a6 100644 --- a/src/modules/identity/presentation/dto/create-user.dto.ts +++ b/src/modules/identity/presentation/dto/create-user.dto.ts @@ -1,11 +1,11 @@ -import { IsEmail, IsNotEmpty, IsString, Length } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty, IsString, Length, IsOptional, IsInt, Min } from 'class-validator'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class CreateUserDto { @ApiProperty({ example: 'John Doe' }) @IsString() @IsNotEmpty() - name!: string; + username!: string; @ApiProperty({ example: 'user@example.com' }) @IsEmail() @@ -16,6 +16,15 @@ export class CreateUserDto { @Length(8, 128) password!: string; - @ApiProperty({ required: false }) - metadata?: Record; + @ApiPropertyOptional({ example: 10737418240 }) + @IsOptional() + @IsInt() + @Min(0) + storageQuota?: number; + + @ApiPropertyOptional({ example: 0 }) + @IsOptional() + @IsInt() + @Min(0) + storageUsed?: number; } \ No newline at end of file diff --git a/src/modules/identity/presentation/dto/update-permission.dto.ts b/src/modules/identity/presentation/dto/update-permission.dto.ts new file mode 100644 index 0000000..a942d25 --- /dev/null +++ b/src/modules/identity/presentation/dto/update-permission.dto.ts @@ -0,0 +1,21 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreatePermissionDto } from './create-permission.dto'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdatePermissionDto extends PartialType(CreatePermissionDto) { + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + code?: string; +} diff --git a/src/modules/identity/presentation/dto/update-role.dto.ts b/src/modules/identity/presentation/dto/update-role.dto.ts new file mode 100644 index 0000000..2a72472 --- /dev/null +++ b/src/modules/identity/presentation/dto/update-role.dto.ts @@ -0,0 +1,20 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateRoleDto } from './create-role.dto'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateRoleDto extends PartialType(CreateRoleDto) { + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional() + @IsOptional() + isDefault?: boolean; +} diff --git a/src/modules/identity/presentation/dto/update-user.dto.ts b/src/modules/identity/presentation/dto/update-user.dto.ts index 1dbe93f..968d85a 100644 --- a/src/modules/identity/presentation/dto/update-user.dto.ts +++ b/src/modules/identity/presentation/dto/update-user.dto.ts @@ -1,7 +1,7 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateUserDto } from './create-user.dto'; import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IsOptional, IsString, IsEmail, Length } from 'class-validator'; +import { IsOptional, IsString, IsEmail, Length, IsInt, Min } from 'class-validator'; export class UpdateUserDto extends PartialType(CreateUserDto) { @ApiPropertyOptional() @@ -22,4 +22,16 @@ export class UpdateUserDto extends PartialType(CreateUserDto) { @ApiPropertyOptional() @IsOptional() metadata?: Record; + + @ApiPropertyOptional({ example: 10737418240 }) + @IsOptional() + @IsInt() + @Min(0) + storageQuota?: number; + + @ApiPropertyOptional({ example: 0 }) + @IsOptional() + @IsInt() + @Min(0) + storageUsed?: number; } diff --git a/src/modules/identity/presentation/guards/current-user.guard.ts b/src/modules/identity/presentation/guards/current-user.guard.ts new file mode 100644 index 0000000..f26fcd6 --- /dev/null +++ b/src/modules/identity/presentation/guards/current-user.guard.ts @@ -0,0 +1,27 @@ +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { SyncIdentityHandler } from '../../application/handlers/user/sync-identity.handler'; +import { IdentityData } from '../../../../core/auth/interfaces/identity-data'; + +@Injectable() +export class CurrentUserGuard implements CanActivate { + constructor( + private readonly syncIdentityHandler: SyncIdentityHandler, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() as any; + + const identity = request.identity as IdentityData | undefined; + + if (!identity) { + throw new UnauthorizedException('Identity is missing.'); + } + + const user = await this.syncIdentityHandler.execute(identity); + + // attach domain user as currentUser + request.currentUser = user; + + return true; + } +} diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..d8b6315 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,7 @@ +{ + "status": "failed", + "failedTests": [ + "c6e079ee66bcf8f961dc-c23b04c7b0e0ed54fd6c", + "c6e079ee66bcf8f961dc-eb7b68d84e422e1710d8" + ] +} \ No newline at end of file diff --git a/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md b/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md new file mode 100644 index 0000000..9978184 --- /dev/null +++ b/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md @@ -0,0 +1,193 @@ +# Instructions + +- Following Playwright test failed. +- Explain why, be concise, respect Playwright best practices. +- Provide a snippet of code with the fix, if possible. + +# Test info + +- Name: user.integration.spec.ts >> Integration - User lifecycle >> login via Authentik creates local user and issues internal JWT +- Location: tests\integration\user.integration.spec.ts:82:7 + +# Error details + +``` +Error: No jwt cookie set after login. Saved screenshot/cookies/page to tests/test-artifacts +``` + +# Test source + +```ts + 19 | // increase default timeout for slow integration flows + 20 | test.setTimeout(60000); + 21 | + 22 | async function adminLogin() { + 23 | const adminUser = process.env.ADMIN_USERNAME; + 24 | const adminPass = process.env.ADMIN_PASSWORD; + 25 | if (!adminUser || !adminPass) throw new Error('ADMIN_USERNAME/ADMIN_PASSWORD must be set in env for integration tests'); + 26 | + 27 | const browser = await chromium.launch({ headless: true }); + 28 | const context = await browser.newContext(); + 29 | const page = await context.newPage(); + 30 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 31 | + 32 | try { + 33 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 34 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 35 | if (usernameInput) await usernameInput.fill(adminUser); + 36 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 37 | if (passwordInput) await passwordInput.fill(adminPass); + 38 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 39 | if (submitButton) { + 40 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 41 | } + 42 | } catch (e) { + 43 | // ignore if login form not present + 44 | } + 45 | + 46 | await page.waitForTimeout(1000); + 47 | const cookies = await context.cookies(); + 48 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 49 | await context.close(); + 50 | await browser.close(); + 51 | + 52 | if (!jwtCookie) throw new Error('Admin login failed: no session cookie'); + 53 | return jwtCookie.value; + 54 | } + 55 | + 56 | async function apiRequest(path, token, opts = {}) { + 57 | const headers = Object.assign({ 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, opts.headers || {}); + 58 | const method = opts.method || 'GET'; + 59 | const body = opts.body ? JSON.stringify(opts.body) : undefined; + 60 | let url; + 61 | try { + 62 | url = new URL(path, BASE_URL).toString(); + 63 | } catch (e) { + 64 | throw new Error(`Invalid BASE_URL for integration tests: ${BASE_URL}`); + 65 | } + 66 | const res = await fetch(url, { method, headers, body }); + 67 | let json = null; + 68 | try { json = await res.json(); } catch (e) { json = null; } + 69 | return { status: res.status, body: json }; + 70 | } + 71 | + 72 | + 73 | test.describe('Integration - User lifecycle', () => { + 74 | let adminToken; + 75 | let createdUser = null; + 76 | + 77 | test.beforeAll(async () => { + 78 | // Integration tests assume test users are created in Authentik prior to running. + 79 | // RayLab will create local user record upon first successful login via Authentik. + 80 | }); + 81 | + 82 | test('login via Authentik creates local user and issues internal JWT', async () => { + 83 | // use browser flow to login as the test user + 84 | const browser = await chromium.launch({ headless: true }); + 85 | const context = await browser.newContext(); + 86 | const page = await context.newPage(); + 87 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 88 | + 89 | try { + 90 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 91 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 92 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 93 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 94 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 95 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 96 | if (submitButton) { + 97 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 98 | } + 99 | } catch (e) { + 100 | // ignore if login form not present + 101 | } + 102 | + 103 | await page.waitForTimeout(1000); + 104 | const cookies = await context.cookies(); + 105 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 106 | const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + 107 | + 108 | if (!jwtCookie) { + 109 | const fs = require('fs'); + 110 | const dir = 'tests/test-artifacts'; + 111 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 112 | const ts = Date.now(); + 113 | await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + 114 | fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 115 | const content = await page.content(); + 116 | fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + 117 | await context.close(); + 118 | await browser.close(); +> 119 | throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + | ^ Error: No jwt cookie set after login. Saved screenshot/cookies/page to tests/test-artifacts + 120 | } + 121 | + 122 | // ensure we have an internal jwt cookie + 123 | expect(jwtCookie).toBeDefined(); + 124 | expect(jwtCookie.value).toBeTruthy(); + 125 | + 126 | // validate /auth/me using the internal jwt + 127 | const token = jwtCookie.value; + 128 | const meUrl = new URL('/auth/me', BASE_URL).toString(); + 129 | const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + 130 | const meJson = await meResp.json(); + 131 | expect(meResp.status).toBeLessThan(300); + 132 | expect(meJson.success).toBe(true); + 133 | expect(meJson.data).toBeDefined(); + 134 | expect(meJson.data.email).toBe(TEST_USER_EMAIL); + 135 | + 136 | await context.close(); + 137 | await browser.close(); + 138 | }); + 139 | + 140 | test('login via Authentik (created user) issues internal JWT', async () => { + 141 | // use browser flow to login as the created user + 142 | const browser = await chromium.launch({ headless: true }); + 143 | const context = await browser.newContext(); + 144 | const page = await context.newPage(); + 145 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 146 | + 147 | try { + 148 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 149 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 150 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 151 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 152 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 153 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 154 | if (submitButton) { + 155 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 156 | } + 157 | } catch (e) { + 158 | // ignore if login form not present + 159 | } + 160 | + 161 | await page.waitForTimeout(1000); + 162 | const cookies = await context.cookies(); + 163 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 164 | + 165 | if (!jwtCookie) { + 166 | const fs = require('fs'); + 167 | const dir = 'tests/test-artifacts'; + 168 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 169 | const ts = Date.now(); + 170 | await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + 171 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 172 | const content = await page.content(); + 173 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + 174 | await context.close(); + 175 | await browser.close(); + 176 | throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + 177 | } + 178 | + 179 | await context.close(); + 180 | await browser.close(); + 181 | + 182 | expect(jwtCookie).toBeDefined(); + 183 | expect(jwtCookie.value).toBeTruthy(); + 184 | }); + 185 | + 186 | // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + 187 | + 188 | }); + 189 | +``` \ No newline at end of file diff --git a/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md b/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md new file mode 100644 index 0000000..18988bc --- /dev/null +++ b/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md @@ -0,0 +1,136 @@ +# Instructions + +- Following Playwright test failed. +- Explain why, be concise, respect Playwright best practices. +- Provide a snippet of code with the fix, if possible. + +# Test info + +- Name: user.integration.spec.ts >> Integration - User lifecycle >> login via Authentik (created user) issues internal JWT +- Location: tests\integration\user.integration.spec.ts:140:7 + +# Error details + +``` +Error: No jwt cookie set after login (second test). Saved screenshot/cookies/page to tests/test-artifacts +``` + +# Test source + +```ts + 76 | + 77 | test.beforeAll(async () => { + 78 | // Integration tests assume test users are created in Authentik prior to running. + 79 | // RayLab will create local user record upon first successful login via Authentik. + 80 | }); + 81 | + 82 | test('login via Authentik creates local user and issues internal JWT', async () => { + 83 | // use browser flow to login as the test user + 84 | const browser = await chromium.launch({ headless: true }); + 85 | const context = await browser.newContext(); + 86 | const page = await context.newPage(); + 87 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 88 | + 89 | try { + 90 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 91 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 92 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 93 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 94 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 95 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 96 | if (submitButton) { + 97 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 98 | } + 99 | } catch (e) { + 100 | // ignore if login form not present + 101 | } + 102 | + 103 | await page.waitForTimeout(1000); + 104 | const cookies = await context.cookies(); + 105 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 106 | const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + 107 | + 108 | if (!jwtCookie) { + 109 | const fs = require('fs'); + 110 | const dir = 'tests/test-artifacts'; + 111 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 112 | const ts = Date.now(); + 113 | await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + 114 | fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 115 | const content = await page.content(); + 116 | fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + 117 | await context.close(); + 118 | await browser.close(); + 119 | throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + 120 | } + 121 | + 122 | // ensure we have an internal jwt cookie + 123 | expect(jwtCookie).toBeDefined(); + 124 | expect(jwtCookie.value).toBeTruthy(); + 125 | + 126 | // validate /auth/me using the internal jwt + 127 | const token = jwtCookie.value; + 128 | const meUrl = new URL('/auth/me', BASE_URL).toString(); + 129 | const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + 130 | const meJson = await meResp.json(); + 131 | expect(meResp.status).toBeLessThan(300); + 132 | expect(meJson.success).toBe(true); + 133 | expect(meJson.data).toBeDefined(); + 134 | expect(meJson.data.email).toBe(TEST_USER_EMAIL); + 135 | + 136 | await context.close(); + 137 | await browser.close(); + 138 | }); + 139 | + 140 | test('login via Authentik (created user) issues internal JWT', async () => { + 141 | // use browser flow to login as the created user + 142 | const browser = await chromium.launch({ headless: true }); + 143 | const context = await browser.newContext(); + 144 | const page = await context.newPage(); + 145 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 146 | + 147 | try { + 148 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 149 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 150 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 151 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 152 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 153 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 154 | if (submitButton) { + 155 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 156 | } + 157 | } catch (e) { + 158 | // ignore if login form not present + 159 | } + 160 | + 161 | await page.waitForTimeout(1000); + 162 | const cookies = await context.cookies(); + 163 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 164 | + 165 | if (!jwtCookie) { + 166 | const fs = require('fs'); + 167 | const dir = 'tests/test-artifacts'; + 168 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 169 | const ts = Date.now(); + 170 | await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + 171 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 172 | const content = await page.content(); + 173 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + 174 | await context.close(); + 175 | await browser.close(); +> 176 | throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + | ^ Error: No jwt cookie set after login (second test). Saved screenshot/cookies/page to tests/test-artifacts + 177 | } + 178 | + 179 | await context.close(); + 180 | await browser.close(); + 181 | + 182 | expect(jwtCookie).toBeDefined(); + 183 | expect(jwtCookie.value).toBeTruthy(); + 184 | }); + 185 | + 186 | // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + 187 | + 188 | }); + 189 | +``` \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..8f482be --- /dev/null +++ b/tests/README.md @@ -0,0 +1,12 @@ +Playwright API Testing for RayLab Core + +Structure: +- helpers: auth/request helpers +- fixtures: reusable data helpers +- api: per-controller tests +- reporter: custom reporter writing tests/output/latest-result.txt + +Run: + npx playwright test + +Ensure .env is configured with BASE_URL and identity provider (Authentik or compatible) details. diff --git a/src/modules/identity/domain/.gitkeep b/tests/api/permissions/.gitkeep similarity index 100% rename from src/modules/identity/domain/.gitkeep rename to tests/api/permissions/.gitkeep diff --git a/src/modules/identity/events/.gitkeep b/tests/api/roles/.gitkeep similarity index 100% rename from src/modules/identity/events/.gitkeep rename to tests/api/roles/.gitkeep diff --git a/tests/api/users/enable-user.spec.ts b/tests/api/users/enable-user.spec.ts new file mode 100644 index 0000000..623dc56 --- /dev/null +++ b/tests/api/users/enable-user.spec.ts @@ -0,0 +1,11 @@ +import test from '@playwright/test'; +import { requestPatch } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - PATCH /users/:id/enable', () => { + test('should return 404 for non-existing user', async () => { + const res = await requestPatch('/users/non-existing-id/enable'); + expect([404, 400, 500]).toContain(res.status); + }); +}); diff --git a/tests/api/users/get-current-user.spec.ts b/tests/api/users/get-current-user.spec.ts new file mode 100644 index 0000000..e562eba --- /dev/null +++ b/tests/api/users/get-current-user.spec.ts @@ -0,0 +1,20 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - GET /users/me', () => { + test('should return current user when token provided', async () => { + const res = await requestGet('/users/me'); + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + expect(res.body.success).toBe(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.email).toBeTruthy(); + }); + + test('should be unauthorized without token', async () => { + const res = await requestGet('/users/me', null); + expect(res.status).toBe(401); + }); +}); diff --git a/tests/api/users/get-user.spec.ts b/tests/api/users/get-user.spec.ts new file mode 100644 index 0000000..8333624 --- /dev/null +++ b/tests/api/users/get-user.spec.ts @@ -0,0 +1,11 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - GET /users/:id', () => { + test('should return 404 for non-existing user', async () => { + const res = await requestGet('/users/non-existing-id'); + expect([404, 400, 500]).toContain(res.status); + }); +}); diff --git a/tests/api/users/get-users.spec.ts b/tests/api/users/get-users.spec.ts new file mode 100644 index 0000000..4855d82 --- /dev/null +++ b/tests/api/users/get-users.spec.ts @@ -0,0 +1,23 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + + +const { expect } = test; + +test.describe('Users - GET /users', () => { + test('should return list of users (authorized)', async () => { + const res = await requestGet('/users'); + + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + expect(res.body).toBeTruthy(); + expect(res.body.success).toBe(true); + expect(res.body.data).toBeInstanceOf(Array); + expect(res.body.meta).toBeDefined(); + }); + + test('should return unauthorized when missing token', async () => { + const res = await requestGet('/users', null); + expect(res.status).toBe(401); + }); +}); diff --git a/tests/fixtures/user.fixture.ts b/tests/fixtures/user.fixture.ts new file mode 100644 index 0000000..74d47ae --- /dev/null +++ b/tests/fixtures/user.fixture.ts @@ -0,0 +1,11 @@ +import { requestPost, requestDelete } from '../helpers/request'; + +export async function createDummyUser(payload: any) { + const res = await requestPost('/user', 'admin', payload); + return res; +} + +export async function deleteDummyUser(userId: string) { + // soft delete + await requestDelete(`/users/${userId}`, 'admin'); +} diff --git a/tests/helpers/auth.ts b/tests/helpers/auth.ts new file mode 100644 index 0000000..d9590eb --- /dev/null +++ b/tests/helpers/auth.ts @@ -0,0 +1,91 @@ +import { chromium } from 'playwright'; +import { env } from './env'; + +const store: Map = new Map(); + +function setToken(key: string, token: string, expiresIn?: number) { + const expiresAt = expiresIn ? Date.now() + expiresIn * 1000 - 5000 : undefined; + store.set(key, { token, expiresAt }); +} + +function getTokenFromStore(key: string) { + const entry = store.get(key); + if (!entry) return null; + if (entry.expiresAt && Date.now() > entry.expiresAt) { + store.delete(key); + return null; + } + return entry.token; +} + +async function loginViaRaylab(username: string, password: string) { + const base = env.BASE_URL || 'http://localhost:3000'; + const browser = await chromium.launch({ headless: Boolean(process.env.PW_HEADLESS || '1') }); + const context = await browser.newContext(); + const page = await context.newPage(); + + // Navigate to RayLab - expects redirect to identity provider + await page.goto(base, { waitUntil: 'networkidle' }); + + // Wait for a login form to appear on identity provider + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + // fill username + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(username); + + // fill password + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(password); + + // try to submit + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // If no login form found, we may already be at callback + } + + // Wait for callback to be done and cookie to be set + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + + const result: any = {}; + if (jwtCookie) result.accessToken = jwtCookie.value; + if (refreshCookie) result.refreshToken = refreshCookie.value; + + await context.close(); + await browser.close(); + + if (!result.accessToken) throw new Error('Login failed: no session cookie set'); + + return result; +} + +export async function getToken(role: 'owner' | 'admin' | 'employee' = 'admin') { + const cacheKey = `role:${role}`; + const cached = getTokenFromStore(cacheKey); + if (cached) return cached; + + const username = role === 'owner' ? env.OWNER_USERNAME : role === 'admin' ? env.ADMIN_USERNAME : env.EMPLOYEE_USERNAME; + const password = role === 'owner' ? env.OWNER_PASSWORD : role === 'admin' ? env.ADMIN_PASSWORD : env.EMPLOYEE_PASSWORD; + + if (!username || !password) throw new Error('Missing credentials in .env for role: ' + role); + + const data = await loginViaRaylab(username, password); + const token = data.accessToken; + // expiresIn not available from cookie - default to 1 hour + const expiresIn = Number(process.env.TEST_TOKEN_EXPIRES_IN || 3600); + setToken(cacheKey, token, expiresIn); + return token; +} + +export async function getRawToken(username: string, password: string) { + const data = await loginViaRaylab(username, password); + return data.accessToken; +} + +export default { getToken, getRawToken }; diff --git a/tests/helpers/env.ts b/tests/helpers/env.ts new file mode 100644 index 0000000..eab59dd --- /dev/null +++ b/tests/helpers/env.ts @@ -0,0 +1,7 @@ +import dotenv from 'dotenv'; +import path from 'path'; + +const envPath = path.resolve(process.cwd(), '.env'); +dotenv.config({ path: envPath }); + +export const env = process.env; diff --git a/tests/helpers/index.ts b/tests/helpers/index.ts new file mode 100644 index 0000000..63ad0e0 --- /dev/null +++ b/tests/helpers/index.ts @@ -0,0 +1,3 @@ +export * from './auth'; +export * from './request'; +export * from './env'; diff --git a/tests/helpers/request.ts b/tests/helpers/request.ts new file mode 100644 index 0000000..eb92b0d --- /dev/null +++ b/tests/helpers/request.ts @@ -0,0 +1,73 @@ +import { request } from '@playwright/test'; +import { env } from './env'; +import { getToken } from './auth'; + +async function buildContext(token?: string) { + const headers: any = { 'Content-Type': 'application/json' }; + if (token) headers['Authorization'] = `Bearer ${token}`; + + const api = await request.newContext({ baseURL: env.BASE_URL || 'http://localhost:3000', extraHTTPHeaders: headers }); + return api; +} + +export async function requestGet(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.get(path, opts); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestPost(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', data?: any, opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.post(path, { data, ...opts }); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestPatch(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', data?: any, opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.patch(path, { data, ...opts }); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestDelete(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.delete(path, opts); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +async function parseResponseSafe(res: any) { + const ct = res.headers()['content-type'] || ''; + try { + if (ct.includes('application/json')) return await res.json(); + return await res.text(); + } catch (e) { + try { + return await res.text(); + } catch (e2) { + return null; + } + } +} diff --git a/tests/integration/user.integration.spec.ts b/tests/integration/user.integration.spec.ts new file mode 100644 index 0000000..62bf886 --- /dev/null +++ b/tests/integration/user.integration.spec.ts @@ -0,0 +1,188 @@ +import dotenv from 'dotenv'; +import fetch from 'node-fetch'; +import { test, expect } from '@playwright/test'; +import { chromium } from 'playwright'; + +dotenv.config(); + +const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL || 'test-integration@example.com'; +const TEST_USER_USERNAME = process.env.TEST_USER_USERNAME || 'test-integration'; +const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD || 'StrongP@ssw0rd!'; + +let BASE_URL = (process.env.BASE_URL || 'http://localhost:3000').trim(); +// sometimes env can contain accidental concatenated vars; take first token +BASE_URL = BASE_URL.split(/\s+/)[0]; +if (!BASE_URL.startsWith('http://') && !BASE_URL.startsWith('https://')) { + BASE_URL = 'http://' + BASE_URL; +} + +// increase default timeout for slow integration flows +test.setTimeout(60000); + +async function adminLogin() { + const adminUser = process.env.ADMIN_USERNAME; + const adminPass = process.env.ADMIN_PASSWORD; + if (!adminUser || !adminPass) throw new Error('ADMIN_USERNAME/ADMIN_PASSWORD must be set in env for integration tests'); + + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(adminUser); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(adminPass); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + await context.close(); + await browser.close(); + + if (!jwtCookie) throw new Error('Admin login failed: no session cookie'); + return jwtCookie.value; +} + +async function apiRequest(path, token, opts = {}) { + const headers = Object.assign({ 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, opts.headers || {}); + const method = opts.method || 'GET'; + const body = opts.body ? JSON.stringify(opts.body) : undefined; + let url; + try { + url = new URL(path, BASE_URL).toString(); + } catch (e) { + throw new Error(`Invalid BASE_URL for integration tests: ${BASE_URL}`); + } + const res = await fetch(url, { method, headers, body }); + let json = null; + try { json = await res.json(); } catch (e) { json = null; } + return { status: res.status, body: json }; +} + + +test.describe('Integration - User lifecycle', () => { + let adminToken; + let createdUser = null; + + test.beforeAll(async () => { + // Integration tests assume test users are created in Authentik prior to running. + // RayLab will create local user record upon first successful login via Authentik. + }); + + test('login via Authentik creates local user and issues internal JWT', async () => { + // use browser flow to login as the test user + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + + if (!jwtCookie) { + const fs = require('fs'); + const dir = 'tests/test-artifacts'; + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const ts = Date.now(); + await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + const content = await page.content(); + fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + await context.close(); + await browser.close(); + throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + } + + // ensure we have an internal jwt cookie + expect(jwtCookie).toBeDefined(); + expect(jwtCookie.value).toBeTruthy(); + + // validate /auth/me using the internal jwt + const token = jwtCookie.value; + const meUrl = new URL('/auth/me', BASE_URL).toString(); + const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + const meJson = await meResp.json(); + expect(meResp.status).toBeLessThan(300); + expect(meJson.success).toBe(true); + expect(meJson.data).toBeDefined(); + expect(meJson.data.email).toBe(TEST_USER_EMAIL); + + await context.close(); + await browser.close(); + }); + + test('login via Authentik (created user) issues internal JWT', async () => { + // use browser flow to login as the created user + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + + if (!jwtCookie) { + const fs = require('fs'); + const dir = 'tests/test-artifacts'; + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const ts = Date.now(); + await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + const content = await page.content(); + fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + await context.close(); + await browser.close(); + throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + } + + await context.close(); + await browser.close(); + + expect(jwtCookie).toBeDefined(); + expect(jwtCookie.value).toBeTruthy(); + }); + + // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + +}); diff --git a/src/modules/identity/infrastructure/mappers/.gitkeep b/tests/output/.gitkeep similarity index 100% rename from src/modules/identity/infrastructure/mappers/.gitkeep rename to tests/output/.gitkeep diff --git a/tests/output/latest-result.txt b/tests/output/latest-result.txt new file mode 100644 index 0000000..32528bb --- /dev/null +++ b/tests/output/latest-result.txt @@ -0,0 +1 @@ +TEST RUN FAILED OR ABORTED \ No newline at end of file diff --git a/tests/reporter/custom-reporter.js b/tests/reporter/custom-reporter.js new file mode 100644 index 0000000..3e3569d --- /dev/null +++ b/tests/reporter/custom-reporter.js @@ -0,0 +1,72 @@ +const fs = require('fs'); + +class CustomReporter { + onEnd(config, result) { + const outputPath = 'tests/output/latest-result.txt'; + + if (!result) { + // test run aborted or failed to initialize + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + fs.writeFileSync(outputPath, 'TEST RUN FAILED OR ABORTED', 'utf-8'); + return; + } + + const failed = result.status === 'failed' || result.numFailedTests > 0; + + const date = new Date().toISOString(); + const duration = (result.duration || 0) + 'ms'; + + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + + if (!failed) { + const content = [ + '==================================', + 'TEST RESULT', + 'PASSED', + `Date: ${date}`, + `Total Test: ${result.total || 0}`, + `Passed: ${result.passed || 0}`, + `Failed: ${result.failed || 0}`, + `Duration: ${duration}`, + '==================================', + ].join('\n'); + + fs.writeFileSync(outputPath, content, 'utf-8'); + return; + } + + const lines = []; + lines.push('=================================='); + lines.push('FAILED'); + lines.push(`Date: ${date}`); + lines.push(`Duration: ${duration}`); + lines.push('=================================='); + + const buildFailureDetails = (suites) => { + for (const s of suites) { + if (s.suites && s.suites.length) buildFailureDetails(s.suites); + if (s.tests && s.tests.length) { + for (const t of s.tests) { + if (t.status === 'failed') { + lines.push('Test Name: ' + (t.title || t.titleText || '')); + for (const r of t.results || []) { + if (r.status === 'failed') { + const error = r.error || {}; + lines.push('Error Message: ' + (error.message || '')); + if (error.stack) lines.push('Stack Trace: ' + error.stack); + } + } + lines.push('----------------------------------'); + } + } + } + } + }; + + if (result.suites) buildFailureDetails(result.suites); + + fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8'); + } +} + +module.exports = CustomReporter; diff --git a/tests/reporter/custom-reporter.ts b/tests/reporter/custom-reporter.ts new file mode 100644 index 0000000..fff0d6a --- /dev/null +++ b/tests/reporter/custom-reporter.ts @@ -0,0 +1,78 @@ +import fs from 'fs'; +import { FullConfig, Reporter, Suite, TestCase, TestError } from '@playwright/test/reporter'; + +class CustomReporter implements Reporter { + onEnd(config: FullConfig, result: any) { + const outputPath = 'tests/output/latest-result.txt'; + + const allTests = result.suites || []; + + const failed = result.status === 'failed' || result.numFailedTests > 0; + + const date = new Date().toISOString(); + const duration = (result.duration || 0) + 'ms'; + + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + + if (!failed) { + const content = [ + '==================================', + 'TEST RESULT', + 'PASSED', + `Date: ${date}`, + `Total Test: ${result.total || 0}`, + `Passed: ${result.passed || 0}`, + `Failed: ${result.failed || 0}`, + `Duration: ${duration}`, + '==================================', + ].join('\n'); + + fs.writeFileSync(outputPath, content, 'utf-8'); + return; + } + + // Failed: build detailed report + const lines: string[] = []; + lines.push('=================================='); + lines.push('FAILED'); + lines.push(`Date: ${date}`); + lines.push(`Duration: ${duration}`); + lines.push('=================================='); + + for (const res of result.report || []) { + // older Playwright may not provide report; fallback to result.annotations + } + + // Walk tests + const buildFailureDetails = (suites: any[]) => { + for (const s of suites) { + if (s.suites && s.suites.length) buildFailureDetails(s.suites); + if (s.tests && s.tests.length) { + for (const t of s.tests) { + if (t.status === 'failed') { + lines.push('Test Name: ' + t.title.join(' > ')); + // try extract location + const location = t.location ? `${t.location.file}:${t.location.line}` : ''; + if (location) lines.push('Location: ' + location); + for (const r of t.results || []) { + if (r.status === 'failed') { + const error = r.error as TestError | undefined; + lines.push('Error Message: ' + (error?.message || '')); + if (r.stdout && r.stdout.length) lines.push('Stdout: ' + r.stdout.join('\n')); + if (error?.stack) lines.push('Stack Trace: ' + error.stack); + } + } + lines.push('----------------------------------'); + } + } + } + } + }; + + if (result.suites) buildFailureDetails(result.suites); + + fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8'); + } +} + +export default CustomReporter; diff --git a/tests/test-artifacts/failed-login-1785604157244-cookies.json b/tests/test-artifacts/failed-login-1785604157244-cookies.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/test-artifacts/failed-login-1785604157244-cookies.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/test-artifacts/failed-login-1785604157244-page.html b/tests/test-artifacts/failed-login-1785604157244-page.html new file mode 100644 index 0000000..fd764ba --- /dev/null +++ b/tests/test-artifacts/failed-login-1785604157244-page.html @@ -0,0 +1 @@ +
{"message":"Cannot GET /","error":"Not Found","statusCode":404}
\ No newline at end of file diff --git a/tests/test-artifacts/failed-login-1785604157244.png b/tests/test-artifacts/failed-login-1785604157244.png new file mode 100644 index 0000000..2a57538 Binary files /dev/null and b/tests/test-artifacts/failed-login-1785604157244.png differ diff --git a/tests/test-artifacts/failed-login-2-1785604169592-cookies.json b/tests/test-artifacts/failed-login-2-1785604169592-cookies.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/test-artifacts/failed-login-2-1785604169592-cookies.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/test-artifacts/failed-login-2-1785604169592-page.html b/tests/test-artifacts/failed-login-2-1785604169592-page.html new file mode 100644 index 0000000..fd764ba --- /dev/null +++ b/tests/test-artifacts/failed-login-2-1785604169592-page.html @@ -0,0 +1 @@ +
{"message":"Cannot GET /","error":"Not Found","statusCode":404}
\ No newline at end of file diff --git a/tests/test-artifacts/failed-login-2-1785604169592.png b/tests/test-artifacts/failed-login-2-1785604169592.png new file mode 100644 index 0000000..2a57538 Binary files /dev/null and b/tests/test-artifacts/failed-login-2-1785604169592.png differ diff --git a/tests/unit/user.service.spec.ts b/tests/unit/user.service.spec.ts new file mode 100644 index 0000000..d8ad0a1 --- /dev/null +++ b/tests/unit/user.service.spec.ts @@ -0,0 +1,41 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UserService } from '../../src/modules/identity/application/services/user.service'; +import { PrismaService } from '../../src/shared/prisma.service'; +import { IUser } from '../../src/modules/identity/domain/repositories/user.interface'; + +describe('UserService', () => { + let service: UserService; + let prisma: Partial; + let userRepo: Partial; + + beforeEach(async () => { + prisma = { + user: { + update: jest.fn().mockResolvedValue(null), + findMany: jest.fn().mockResolvedValue([]), + }, + } as any; + + userRepo = { + create: jest.fn().mockImplementation(async (user) => { + return { ...user, id: 'user-1' }; + }), + getById: jest.fn().mockResolvedValue({ id: 'user-1', username: 'u', email: 'e' }), + existByEmail: jest.fn().mockResolvedValue(false), + } as any; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + UserService, + { provide: PrismaService, useValue: prisma }, + { provide: IUser, useValue: userRepo }, + ], + }).compile(); + + service = module.get(UserService); + }); + + it('createUser - provisioning disabled', async () => { + await expect(service.createUser({ username: 'u', email: 'e' })).rejects.toThrow(); + }); +});