From 055a25de8027eb652e489d3822d284bfd0d0eb59 Mon Sep 17 00:00:00 2001 From: benedictworks-home Date: Wed, 30 Sep 2026 09:30:18 -0700 Subject: [PATCH 1/3] fix: restore 55 source files emptied by recent merges A run of merge commits between 09:48 and 11:28 today left 55 tracked files at zero bytes on main, including package.json, prisma/schema.prisma, src/app.module.ts, src/intents/intents.gateway.ts and src/config/env.validation.ts. CI on main is red because of it. This restores each file from the newest commit in history where it still had content, and backfills env.validation.ts with the 29 keys that .env*.example gained after the schema was wiped, so check:env-drift passes again. Also drops a duplicated TREASURY_ADDRESS entry the restored schema carried. --- .env.testnet.example | 198 + package-lock.json | 27891 ++++++++++++++++ package.json | 116 + prisma/schema.prisma | 599 + src/app.module.ts | 89 + src/common/stellar-signature.ts | 179 + src/config/configuration.ts | 529 + src/config/env.validation.ts | 549 + src/governance/governance.module.ts | 32 + src/intents/dto/cancel-intent.dto.ts | 29 + src/intents/dto/create-intent.dto.ts | 179 + src/intents/dto/fill-intent.dto.ts | 49 + src/intents/dto/list-intents.dto.ts | 130 + src/intents/intents-sweeper.service.ts | 252 + src/intents/intents.controller.ts | 760 + src/intents/intents.gateway.spec.ts | 731 + src/intents/intents.gateway.ts | 1108 + src/intents/intents.module.ts | 100 + src/intents/intents.service.shadow.spec.ts | 414 + src/intents/intents.service.spec.ts | 547 + src/intents/intents.types.ts | 151 + src/intents/prisma-intents.repository.ts | 486 + src/intents/solver-intent-matcher.ts | 164 + src/intents/ws/connection-state.ts | 128 + src/metrics/metrics.service.ts | 548 + src/solvers/solvers.controller.ts | 395 + src/soroban/contracts/settlement.client.ts | 230 + src/soroban/event-ingestion.service.spec.ts | 138 + src/soroban/event-ingestion.service.ts | 351 + src/soroban/fill-verifier.service.spec.ts | 63 + src/soroban/fill-verifier.service.ts | 89 + src/soroban/signer.service.spec.ts | 215 + src/soroban/signer.service.ts | 132 + src/soroban/solver-registry.service.spec.ts | 212 + src/soroban/soroban.controller.spec.ts | 174 + src/soroban/soroban.module.ts | 76 + src/soroban/soroban.service.ts | 103 + src/soroban/stellar-tx.service.spec.ts | 591 + src/soroban/stellar-tx.service.ts | 708 + src/soroban/tx-confirmation.service.spec.ts | 203 + src/soroban/tx-confirmation.service.ts | 116 + src/stats/stats.service.ts | 215 + src/tokens/in-memory-tokens.repository.ts | 49 + src/tokens/tokens.service.spec.ts | 181 + src/tokens/tokens.service.ts | 222 + src/treasury/treasury.service.spec.ts | 520 + src/treasury/treasury.service.ts | 538 + test/__mocks__/@stellar/stellar-sdk.ts | 119 + test/cors.e2e-spec.ts | 192 + test/dos-limits.e2e-spec.ts | 297 + test/jest-e2e.json | 13 + test/load/concurrent-accept.test.ts | 282 + .../load/concurrent-idempotent-create.test.ts | 88 + test/load/ws-broadcast-fanout.test.ts | 281 + test/params.e2e-spec.ts | 179 + 55 files changed, 42930 insertions(+) diff --git a/.env.testnet.example b/.env.testnet.example index e69de29b..304a7ce1 100644 --- a/.env.testnet.example +++ b/.env.testnet.example @@ -0,0 +1,198 @@ +# .env.testnet.example +# +# Environment template for LOCAL DEVELOPMENT against Stellar TESTNET. +# Copy to .env and fill in any values marked with . +# +# cp .env.testnet.example .env +# +# Testnet is safe to experiment with — tokens have no real value and contract +# deployments are free via Friendbot. Never reuse testnet keys on mainnet. +# +# Closes #136 + +# ─── Database ──────────────────────────────────────────────────────────────── +# Local Docker Compose default. Adjust if you use a remote or managed DB. +DATABASE_URL=postgresql://vortex:vortex@localhost:5432/vortex?schema=public + +# ─── Server ────────────────────────────────────────────────────────────────── +PORT=4000 +NODE_ENV=development + +# ─── Stellar / Soroban ─────────────────────────────────────────────────────── +STELLAR_NETWORK=testnet +SOROBAN_RPC_URL=https://soroban-testnet.stellar.org + +# Testnet contract IDs — leave blank until you have deployed contracts. +# The service boots without them; on-chain write paths are no-ops when empty. +SETTLEMENT_CONTRACT_ID= +SOLVER_REGISTRY_CONTRACT_ID= + +# Testnet signing key — generate a throwaway keypair, fund it with Friendbot, +# and paste the secret seed here. Never reuse this key on mainnet. +# +# # Generate a new key: +# npx @stellar/stellar-cli keys generate local-dev --network testnet +# npx @stellar/stellar-cli keys show local-dev +# +# # Or via the SDK: +# node -e "console.log(require('@stellar/stellar-sdk').Keypair.random().secret())" +# +# # Fund it (testnet only): +# curl "https://friendbot.stellar.org/?addr=" +# +# Optional in development — leave blank to skip on-chain writes. +SOROBAN_SIGNING_KEY= + +# Fee percentile used when estimating Soroban inclusion fees. +# p50 is a safe default for testnet; raise to p90+ for time-sensitive mainnet txs. +SOROBAN_FEE_PERCENTILE=p50 + +# ─── CORS ──────────────────────────────────────────────────────────────────── +# Wildcard is fine for local development — tighten this in staging/production. +CORS_ORIGIN=* + +# ─── WebSocket ─────────────────────────────────────────────────────────────── +WS_MAX_CONNECTIONS=1000 + +# ─── Pluggable signer backend (issue #400) ─────────────────────────────────── +# SIGNER_BACKEND=local is the default for development. +# In production use SIGNER_BACKEND=vault and supply VAULT_ADDR + VAULT_TOKEN. +SIGNER_BACKEND=local +VAULT_ADDR= +VAULT_TOKEN= +VAULT_TRANSIT_KEY_NAME=vortex-signer +ALLOW_LOCAL_SIGNER_IN_PROD=false +# ─── Resource-exhaustion limits (issue #476) ───────────────────────────────── +# Maximum JSON nesting depth — rejects deeply-nested body attacks (default 10). +JSON_MAX_DEPTH=10 +# Maximum chain values in a single WS subscribe message (default 20). +WS_MAX_FILTER_CHAINS=20 +# Maximum active subscriptions per WS connection (default 10). +WS_MAX_SUBSCRIPTIONS=10 +# Postgres statement_timeout for standard queries in ms (default 5000). +DB_QUERY_TIMEOUT_MS=5000 +# Postgres statement_timeout for batch queries in ms (default 10000). +DB_BATCH_QUERY_TIMEOUT_MS=10000 +# Postgres statement_timeout for stats queries in ms (default 15000). +DB_STATS_QUERY_TIMEOUT_MS=15000 + +# Emergency kill-switch (issue #477) +# Postgres-backed so a pause survives a restart and reaches every replica. +KILLSWITCH_OPERATOR_TOKEN= +KILLSWITCH_REDIS_URL= +KILLSWITCH_POLL_MS=2000 +KILLSWITCH_PERSISTENCE=prisma + +# ─── Observability (optional) ──────────────────────────────────────────────── +# Leave blank to disable Sentry error reporting. +SENTRY_DSN= + +# debug | info | warn | error (defaults to "debug" in development) +LOG_LEVEL=debug + +# ── Shadow-mode divergence monitor (issue #401) ───────────────────────── +# Off by default in every environment. It runs read-only `simulateTransaction` +# calls against SETTLEMENT_CONTRACT_ID in parallel with the off-chain intent +# path and never signs or submits anything. +# +# SHADOW_SOURCE_ACCOUNT only has to be a valid Stellar public key: it is used to +# populate the source-account field of the simulated envelope and is never +# signed, never charged a fee and never broadcast. It must still be set, or +# every transition reports "contract_unconfigured". +SHADOW_MODE_ENABLED=false +SHADOW_SAMPLE_RATE=1 +SHADOW_QUEUE_MAX=256 +SHADOW_CONCURRENCY=4 +SHADOW_SOURCE_ACCOUNT= +# ─── Governance / Protocol Parameters ──────────────────────────────────────── +# On-chain governance parameters contract ID — leave blank to use code defaults. +PARAMS_CONTRACT_ID= + +# Poll interval in ms. 30 000 is fine for testnet. +PARAMS_POLL_INTERVAL_MS=30000 +# ─── Leader election ───────────────────────────────────────────────────────── +# Enable for multi-replica testnet deployments. +LEADER_ELECTION_ENABLED=false +LEADER_ELECTION_HEARTBEAT_MS=5000 + +# ─── Background jobs (issue #494) ──────────────────────────────────────────── +# api | worker | all — queue workers only run in "worker" or "all". +PROCESS_ROLE=all +# memory (single-process, dev/test) | bullmq (Redis-backed, uses REDIS_URL) +JOBS_DRIVER=memory +# Grace period for in-flight jobs on SIGTERM before they are returned to the queue. +JOBS_SHUTDOWN_TIMEOUT_MS=25000 + +# ─── Runtime feature flags (issue #495) ────────────────────────────────────── +# Change propagation across instances: memory (single instance) | redis +FLAGS_PUBSUB=memory +# Safety-net cache reload interval (ms) +FLAGS_REFRESH_MS=30000 +# Break-glass pins that win over DB state, e.g. onchain-dry-run=true +FLAG_OVERRIDES= + +# ─── Admin RBAC ────────────────────────────────────────────────────────────── +# Comma-separated id:role:secret (role = admin | superadmin, secret >= 16 chars). +# Sent as the x-admin-key header (the secret part). Empty disables admin APIs. +ADMIN_API_KEYS= + +# ─── Guardian emergency ingestion (issue #507) ─────────────────────────────── +# Guardian / security-council contract ID. Leave blank to disable ingestion. +GUARDIAN_CONTRACT_ID= + +# ─── Synthetic canary (issue #496) ─────────────────────────────────────────── +# Canary user + solver addresses; excluded from public stats and leaderboards. +CANARY_ADDRESSES= +# Egress/SSRF Protection +EGRESS_TIMEOUT_MS=10000 +EGRESS_MAX_REDIRECTS=3 +EGRESS_MAX_BODY_SIZE_BYTES=10485760 +SOROBAN_RPC_ALLOWLIST=soroban-testnet.stellar.org,soroban-rpc.stellar.org +WEBHOOK_ALLOWLIST=hooks.example.com,hooks.trusted.com +ORACLE_ALLOWLIST=oracle.trusted.io +# Oracle minDstAmount gates (issue #434). Slippage/premium in basis points. +MAX_USER_SLIPPAGE_BPS=100 +MAX_PREMIUM_BPS=50 +ORACLE_FAIL_OPEN_MAX_USD=100 +ORACLE_MAX_STALENESS_MS=60000 +# Public anonymised datasets (RFC 0001). Disabled until an operator opts in. +DATASETS_ENABLED=false +DATASETS_ANONYMIZE=true +DATASETS_SALT= +DATASETS_SALT_ROTATION_HOURS=24 +DATASETS_SALT_RETENTION_WINDOWS=2 +DATASETS_PUBLIC_BUCKET=vortex-public-datasets +DATASETS_STORAGE_KIND=memory +DATASETS_LOCAL_DIR=./data/datasets +# ─── WS gateway hardening (issue #455) ─────────────────────────────────────── +# Inbound frames larger than this close the socket (1009). +WS_MAX_PAYLOAD_BYTES=16384 +# Concurrent WS connections per client IP (0 = unlimited). +WS_MAX_CONNECTIONS_PER_IP=20 +# Trusted reverse-proxy hops for X-Forwarded-For (0 = socket address only). +WS_TRUST_PROXY_HOPS=0 +# Inbound token bucket per connection; repeat violators are disconnected. +WS_RATE_LIMIT_PER_SEC=10 +WS_RATE_LIMIT_BURST=20 +WS_RATE_LIMIT_MAX_VIOLATIONS=5 +# Outbound backpressure: messages held per slow consumer, socket buffer +# threshold (bytes), and what to do when the queue is full. +WS_OUTBOUND_QUEUE_MAX=1000 +WS_OUTBOUND_BUFFER_BYTES=1048576 +WS_SLOW_CONSUMER_POLICY=drop_oldest +# HS256 secret for solver JWTs from the SEP-10 auth flow (#442); >= 32 chars. +# Empty disables JWT auth on the WS gateway. +AUTH_JWT_SECRET= + +# ─── Health probes (issue #492) ────────────────────────────────────────────── +# Roles served by this process (api, ws, worker); readiness checks follow them. +SERVICE_ROLES=api,ws,worker +HEALTH_CHECK_INTERVAL_MS=5000 +# Readiness hysteresis: failures before not-ready, successes before ready again. +HEALTH_READY_FAILURE_THRESHOLD=3 +HEALTH_READY_SUCCESS_THRESHOLD=2 +# Liveness fails when event-loop delay exceeds this. +HEALTH_EVENT_LOOP_MAX_LAG_MS=1000 +# Soroban RPC endpoints for the quorum check (default: SOROBAN_RPC_URL). +SOROBAN_RPC_HEALTH_URLS= + diff --git a/package-lock.json b/package-lock.json index e69de29b..9be432ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -0,0 +1,27891 @@ +{ + "name": "vortex-backend", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "vortex-backend", + "version": "0.1.0", + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@bull-board/api": "^9.10.1", + "@bull-board/express": "^9.10.1", + "@msgpack/msgpack": "^3.0.0", + "@nestjs/common": "^11.1.28", + "@nestjs/config": "^4.0.4", + "@nestjs/core": "^11.1.28", + "@nestjs/event-emitter": "^12.0.1", + "@nestjs/platform-express": "^11.1.28", + "@nestjs/platform-ws": "^11.1.28", + "@nestjs/schedule": "^12.0.2", + "@nestjs/swagger": "^11.4.5", + "@nestjs/throttler": "^6.5.0", + "@nestjs/websockets": "^11.1.28", + "@openfeature/core": "^1.12.0", + "@openfeature/server-sdk": "^1.23.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0", + "@opentelemetry/sdk-node": "^0.221.0", + "@opentelemetry/sdk-trace-node": "^2.10.0", + "@prisma/client": "5.22.0", + "@sentry/node": "^10.69.0", + "@stellar/stellar-sdk": "^12.0.0", + "bullmq": "^6.3.9", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "helmet": "^7.1.0", + "ioredis": "^6.0.0", + "joi": "^18.2.9", + "parquetjs": "^0.11.2", + "pg": "8.23.0", + "prom-client": "^15.1.3", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2", + "undici": "^8.11.2", + "uuid": "^10.0.0", + "viem": "^2.44.4", + "winston": "^3.13.0", + "ws": "^8.18.0", + "zod": "^4.6.5" + }, + "devDependencies": { + "@commitlint/cli": "^21.2.3", + "@commitlint/config-conventional": "^21.2.3", + "@msgpack/msgpack": "^3.0.0", + "@nestjs/cli": "^11.0.24", + "@nestjs/schematics": "^11.1.0", + "@nestjs/testing": "^12.1.0", + "@stryker-mutator/core": "^8.0.0", + "@stryker-mutator/jest-runner": "^8.0.0", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.25", + "@types/jest": "^30.0.0", + "@types/node": "^26.6.3", + "@types/parquetjs": "^0.10.6", + "@types/supertest": "^7.2.0", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.5.12", + "@typescript-eslint/eslint-plugin": "^8.70.1", + "@typescript-eslint/parser": "^7.13.0", + "eslint": "^8.57.0", + "fast-check": "^4.10.2", + "husky": "^9.1.7", + "jest": "^30.4.2", + "openapi-typescript": "^7.8.0", + "prisma": "5.22.0", + "supertest": "^7.2.2", + "ts-jest": "^29.4.11", + "tsx": "^4.15.4", + "typescript": "^5.4.5" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.27.tgz", + "integrity": "sha512-3amNzoCVSKd7ah6l6lBQL4onwwJvqvam7FMoQBILrxtW5LB5ezh8gMSPuA4zJjKjoRzf9uoWdlzqv/84I52xZA==", + "dev": true, + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.27.tgz", + "integrity": "sha512-/PZmyAlb2NGWPikRRuiWLdfHQd8Wrx6lX4HqvTcaDhlU43M3T0ud4PH2T3QDp7BzHYY92xtD8iPxX2asg67G1A==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.27", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.27.tgz", + "integrity": "sha512-wHYH6SVXVykhLzovUHtYor3Nl4SpIiITi7r9DQDaKYUD4hpRBx25W6N9eGuakT9Vd5tV/x6wmvQFWQZQwFB7eA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.27", + "@angular-devkit/schematics": "19.2.27", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@apm-js-collab/code-transformer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", + "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/estree": "^1.0.8", + "astring": "^1.9.0", + "esquery": "^1.7.0", + "meriyah": "^6.1.4", + "semifies": "^1.0.0", + "source-map": "^0.6.0" + }, + "bin": { + "code-transformer": "cli.js" + } + }, + "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.3.tgz", + "integrity": "sha512-qNbPwuMZ8f5ZuGj/ttPeB7a6C/S1bB6tNYaEL5vNiRKydSAxa4AU0gxCWgaP4fVju+AuwhcumSFjrEcGF9Dv7Q==", + "license": "MIT", + "dependencies": { + "@apm-js-collab/code-transformer": "^0.18.0", + "es-module-lexer": "^2.1.0", + "magic-string": "^0.30.21", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@apm-js-collab/code-transformer-bundler-plugins/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@apm-js-collab/code-transformer/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@apm-js-collab/tracing-hooks": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", + "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", + "license": "Apache-2.0", + "dependencies": { + "@apm-js-collab/code-transformer": "^0.18.0", + "debug": "^4.4.1", + "module-details-from-path": "^1.0.4" + } + }, + "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, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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, + "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" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "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" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", + "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-explicit-resource-management": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-explicit-resource-management/-/plugin-proposal-explicit-resource-management-7.27.4.tgz", + "integrity": "sha512-1SwtCDdZWQvUU1i7wt/ihP7W38WjC3CSTOHAl+Xnbze8+bbMNjRvRQydnj0k9J1jPqCAZctBFp6NHJXkrVVmEA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-explicit-resource-management instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", + "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "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==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@bull-board/api": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-9.10.1.tgz", + "integrity": "sha512-yaFVInFvQAKHQwW7xxdoWQaC2xW1f8JOAZ5k6Zl8GQBTnlhm6UmeRQUr2Gb6wMgfl5v61p2eSupPow2CEvz0ng==", + "license": "MIT", + "dependencies": { + "redis-info": "^3.1.0", + "valibot": "^1.5.0" + }, + "peerDependencies": { + "@bull-board/ui": "9.10.1", + "bull": "^4.16.5", + "bullmq": "^5.56.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "bull": { + "optional": true + }, + "bullmq": { + "optional": true + } + } + }, + "node_modules/@bull-board/express": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-9.10.1.tgz", + "integrity": "sha512-sUXrFAwbllDNGyieB51Xr7bFa+f7uPdWWPMcArROYlAXsZqrG0HyRPf81bpCX8ZnBGTUcbsEq9jSdRpEfbqm7g==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "9.10.1", + "@bull-board/ui": "9.10.1", + "ejs": "^6.0.1", + "express": "^5.2.1" + } + }, + "node_modules/@bull-board/ui": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-9.10.1.tgz", + "integrity": "sha512-1E0zbLaljf+Zhx3nDUotzsG2Dz7/4i72nWCxKkFwxfz65MKXfObo2mXWMuIDoXAeWSa3sm7hlPj3h3scts4DaQ==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "9.10.1" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.3.tgz", + "integrity": "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-conventional": "^21.2.3", + "@commitlint/format": "^21.2.3", + "@commitlint/lint": "^21.2.3", + "@commitlint/load": "^21.2.3", + "@commitlint/read": "^21.2.3", + "@commitlint/types": "^21.2.3", + "tinyexec": "^1.0.0", + "yargs": "^18.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/cli/node_modules/@commitlint/config-conventional": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", + "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-conventionalcommits": "^10.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/cli/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/cli/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@commitlint/cli/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/conventional-changelog-conventionalcommits": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@commitlint/cli/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@commitlint/cli/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@commitlint/cli/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", + "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-conventionalcommits": "^10.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/config-conventional/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/config-conventional/node_modules/conventional-commits-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.3.tgz", + "integrity": "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/config-validator/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/ensure": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.3.tgz", + "integrity": "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "es-toolkit": "^1.46.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/ensure/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", + "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/format": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.3.tgz", + "integrity": "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/format/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.3.tgz", + "integrity": "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/lint": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.3.tgz", + "integrity": "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^21.2.3", + "@commitlint/parse": "^21.2.3", + "@commitlint/rules": "^21.2.3", + "@commitlint/types": "^21.2.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/lint/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/load": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.3.tgz", + "integrity": "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^21.2.3", + "@commitlint/execute-rule": "^21.0.1", + "@commitlint/resolve-extends": "^21.2.3", + "@commitlint/types": "^21.2.3", + "cosmiconfig": "^9.0.1", + "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", + "is-plain-obj": "^4.1.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/load/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/@commitlint/load/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@commitlint/message": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", + "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/parse": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.3.tgz", + "integrity": "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-angular": "^9.0.0", + "conventional-commits-parser": "^7.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/parse/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/read": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.3.tgz", + "integrity": "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^21.2.0", + "@commitlint/types": "^21.2.3", + "@conventional-changelog/git-client": "^3.0.0", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/read/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.3.tgz", + "integrity": "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^21.2.3", + "@commitlint/types": "^21.2.3", + "es-toolkit": "^1.46.0", + "global-directory": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.3.tgz", + "integrity": "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^21.2.3", + "@commitlint/message": "^21.2.0", + "@commitlint/to-lines": "^21.0.1", + "@commitlint/types": "^21.2.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/rules/node_modules/@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", + "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/top-level": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", + "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@conventional-changelog/git-client": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", + "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^2.0.0", + "@simple-libs/stream-utils": "^2.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "conventional-commits-filter": "^6.0.1", + "conventional-commits-parser": "^7.1.2" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@conventional-changelog/template": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", + "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://regis + "node_modules/@conventional-changelog/template": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", + "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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/eslintrc/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 + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz", + "integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@ioredis/commands": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-2.0.0.tgz", + "integrity": "sha512-vrx0AE/T0h7cRZwfo1M39Cr+ZhZrkf0V8mQN75wucKCxCLD9l/VX6no3gFvrLqD1IlG/1LtzWovqEw3t0Vr9zg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "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, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "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==" + }, + "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, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==" + }, + "node_modules/@msgpack/msgpack": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz", + "integrity": "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.24", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.24.tgz", + "integrity": "sha512-aIHxQLSYtXShifA3zwWIeznEsZnNa3Iz2QRykFj+sl9IcbERBHr5nH87FRgywM+He3NxoF5WazHfR8FsmVeWxw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.27", + "@angular-devkit/schematics": "19.2.27", + "@angular-devkit/schematics-cli": "19.2.27", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.6", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.106.2", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.28.tgz", + "integrity": "sha512-bRImsxibie+AM7xjdwcrm/gr5YeacI65kSBNzTufa1Ib5iwziaY/lqMtRh9THq6pbV4e1HP9aI2ZxGUumnmaoQ==", + "dependencies": { + "file-type": "21.3.4", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.4.tgz", + "integrity": "sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==", + "dependencies": { + "dotenv": "17.4.1", + "dotenv-expand": "12.0.3", + "lodash": "4.18.1" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/core": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.28.tgz", + "integrity": "sha512-06m63xIRj8+l8uOeh/8LnYupGubkyu4f+bPKIadaSui6vK9KpXgoz7HveT1yOVLcEt0M0oCOEW5EuEXZkEmBBQ==", + "dependencies": { + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/event-emitter": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-12.0.1.tgz", + "integrity": "sha512-7MbpgNcxUcDlqcYY/qgZm1H7MfK3uGcRsd8lEjj9ZYvI9qTl6kdkkykpxZ6YjBRH3wlZK5dFLAei8h5qpsHzOw==", + "license": "MIT", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0 || ^12.0.0", + "@nestjs/core": "^11.0.0 || ^12.0.0" + } + }, + "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==", + "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 + } + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.28.tgz", + "integrity": "sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==", + "dependencies": { + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.2.0", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/platform-ws": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/platform-ws/-/platform-ws-11.1.28.tgz", + "integrity": "sha512-anktDtSYoJEpG8zVvOKHhc2+W9jSX0c3CmnuYUICqgwtc2vC/Md9dBWbyxbB8iU5F5dWICC74MQrBxJMfnZh2g==", + "dependencies": { + "tslib": "2.8.1", + "ws": "8.21.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/platform-ws/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@nestjs/schedule": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-12.0.2.tgz", + "integrity": "sha512-5iAvJEtk0njbfTrG4JQr+SXw8ZrqgDM1wJH9nUs6tVLCX5pWGd1LJ0a8b089UT1FDeDWaThRRfSLixFz9zztcA==", + "license": "MIT", + "dependencies": { + "cron": "4.4.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0 || ^12.0.0", + "@nestjs/core": "^11.0.0 || ^12.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz", + "integrity": "sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "comment-json": "5.0.0", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "typescript": ">=4.8.2" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.24.tgz", + "integrity": "sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==", + "dev": true, + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", + "integrity": "sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.24", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/swagger": { + "version": "11.4.6", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.4.6.tgz", + "integrity": "sha512-Le136h2WC7HGsd70+WyK1qrm+Zq7kFxBLkYC1JgAVqNRCt8kNh7bMF7Qkn65D5j2t/aks0+VbWmUVlYIwPrs3A==", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "@nestjs/mapped-types": "2.1.1", + "js-yaml": "5.2.1", + "lodash": "4.18.1", + "path-to-regexp": "8.4.2", + "swagger-ui-dist": "5.32.8" + }, + "peerDependencies": { + "@fastify/static": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/common": "^11.0.1", + "@nestjs/core": "^11.0.1", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/testing": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-12.1.0.tgz", + "integrity": "sha512-OL6ar+p4BRy26YlOk6nqSchGShQajW0jaPSz2tvP7C3dLQcb26Wm0AG2IlvqIBoHuN3zQFMc5DoLAyiosPP4pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^12.0.0", + "@nestjs/core": "^12.0.0", + "@nestjs/microservices": "^12.0.0", + "@nestjs/platform-express": "^12.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@nestjs/throttler": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", + "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0" + } + }, + "node_modules/@nestjs/websockets": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.28.tgz", + "integrity": "sha512-jeyclAURCJTN8S8lctDhfLdiJeDKjZmYWWLav653Fb9hl9c+zx5jPhavI8Xk5++R8u+lX9qzaRxtsjEoxTtjyw==", + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-socket.io": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@openfeature/core": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@openfeature/core/-/core-1.12.0.tgz", + "integrity": "sha512-7PCPzyd1OC19begz30+CRknFB0ChtyaZUhk7YWrk+Bov1fpVw0HYkTXtoxxsvPfDZB9P9WsT7uixN+Z8f9+bcA==", + "license": "Apache-2.0" + }, + "node_modules/@openfeature/server-sdk": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@openfeature/server-sdk/-/server-sdk-1.23.0.tgz", + "integrity": "sha512-JWeLvltJIV0AFgOfbw7hK9b9Rw/5wWq+RMhCIU9sJtAqarxr8hSPglG+JAGGhYdElAz3Qwe9W3ApWyPNkQEyqg==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@openfeature/core": "^1.12.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.79.0.tgz", + "integrity": "sha512-qL53aIjdw56sRDqz6LXD9h15vPTJgPpqv80rbsnRjzhuC9VqZ58fgk/lx0SdECJ2rcu8keeji5ZgjzJwiQZ0fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/instrumentation-amqplib": "^0.68.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.73.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.76.0", + "@opentelemetry/instrumentation-bunyan": "^0.66.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.66.0", + "@opentelemetry/instrumentation-connect": "^0.64.0", + "@opentelemetry/instrumentation-cucumber": "^0.37.0", + "@opentelemetry/instrumentation-dataloader": "^0.38.0", + "@opentelemetry/instrumentation-dns": "^0.64.0", + "@opentelemetry/instrumentation-express": "^0.69.0", + "@opentelemetry/instrumentation-fs": "^0.40.0", + "@opentelemetry/instrumentation-generic-pool": "^0.64.0", + "@opentelemetry/instrumentation-graphql": "^0.69.0", + "@opentelemetry/instrumentation-grpc": "^0.221.0", + "@opentelemetry/instrumentation-hapi": "^0.67.0", + "@opentelemetry/instrumentation-host-metrics": "^0.4.0", + "@opentelemetry/instrumentation-http": "^0.221.0", + "@opentelemetry/instrumentation-ioredis": "^0.69.0", + "@opentelemetry/instrumentation-kafkajs": "^0.30.0", + "@opentelemetry/instrumentation-knex": "^0.65.0", + "@opentelemetry/instrumentation-koa": "^0.69.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.65.0", + "@opentelemetry/instrumentation-memcached": "^0.64.0", + "@opentelemetry/instrumentation-mongodb": "^0.74.0", + "@opentelemetry/instrumentation-mongoose": "^0.67.0", + "@opentelemetry/instrumentation-mysql": "^0.67.0", + "@opentelemetry/instrumentation-mysql2": "^0.67.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.67.0", + "@opentelemetry/instrumentation-net": "^0.65.0", + "@opentelemetry/instrumentation-openai": "^0.19.0", + "@opentelemetry/instrumentation-oracledb": "^0.46.0", + "@opentelemetry/instrumentation-pg": "^0.73.0", + "@opentelemetry/instrumentation-pino": "^0.67.0", + "@opentelemetry/instrumentation-redis": "^0.69.0", + "@opentelemetry/instrumentation-restify": "^0.66.0", + "@opentelemetry/instrumentation-router": "^0.65.0", + "@opentelemetry/instrumentation-runtime-node": "^0.34.0", + "@opentelemetry/instrumentation-socket.io": "^0.68.0", + "@opentelemetry/instrumentation-tedious": "^0.40.0", + "@opentelemetry/instrumentation-undici": "^0.31.0", + "@opentelemetry/instrumentation-winston": "^0.65.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.36.0", + "@opentelemetry/resource-detector-aws": "^2.21.0", + "@opentelemetry/resource-detector-azure": "^0.29.0", + "@opentelemetry/resource-detector-container": "^0.8.12", + "@opentelemetry/resource-detector-gcp": "^0.56.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^2.0.0" + } + }, + "node_modules/@opentelemetry/configuration": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.221.0.tgz", + "integrity": "sha512-uE9y56Zdi9Gt/RdxYnVOo3YmFZkKJJMA0gqtBe8wh8gdtF5Asqe+Oh/TWiDtFb1s+31jNY4CWgnfIB1KOITfFA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "yaml": "^2.8.3" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz", + "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-txG1G0IrYSsKKMeiWZfj/i5cQmWB+h+hf3HzPpF3RqZVwp+iQQEIsv8Vtmzy6RWVdHdJZfygmVrBI39YTBvWcw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.221.0.tgz", + "integrity": "sha512-nKXkr4Tomi6fjYVOf+ytcW3dZAVr4v4Bv5gsT6dr2gvpUPJpKgHB4XbMufMsPotRE3g0XH2GwVVCkN2w6SON+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.221.0.tgz", + "integrity": "sha512-AH6EY+47gXFaWYgG3hfeOneGiE9xIZGtDBk+9g0sM8NZWzsQhhmqPbQQXJzS7pyCh5jRRr2nYNXVrkCmoojRvQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-KOgCtO15FC6C1T/xOqBcr7EyUs7B+7yomGNb5Y97d3s38rPbCCk5sewkmE2b0/itOkQ/PptX8CLlD+kn2mEtTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.221.0.tgz", + "integrity": "sha512-sRfCKbOzgy8xZQV2as0RzIZlnCmCseCKZGLfRcrpo2CBngJDr+rPtX0zkG0+oUCV5kfQPUoW3W3C96Ag3Y/Clg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.221.0.tgz", + "integrity": "sha512-YMF4LveY2I3yhw61rn6nmC9FE8U24IZHPeKU1Duc5+sbwjMd8FwZAwba318ImdThCg/HuVQvhm2y6bfgNPnfYg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.221.0.tgz", + "integrity": "sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-zXminlZedtq9LvOW64CnNkOqk15zV75k8JgtdTuWFge6+jk2m4GmAUm6L2eIiG1o2a2bZxXw2PDrszm+bps0IA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.221.0.tgz", + "integrity": "sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.221.0.tgz", + "integrity": "sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.10.0.tgz", + "integrity": "sha512-7gsvgf0UDoJ4l9ObrwBmz5G/ZogiPk+lq+g5GpLp24YQF/vPM/BSsnOfcLnfinast5ASUgLo78uSC/ObjlnXgg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.221.0.tgz", + "integrity": "sha512-cCk80Z/iRDf/5gfsKMB4f74LqVA5yKETB/9ojPzVW/6/f70iu89nJvGxsFCxx4XfSohaOofkU19kiYm84AiAlw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.68.0.tgz", + "integrity": "sha512-U9Fc3C061q+AGxP3xEJTIAJtBduY1GL21J4SjOSxCmmls4UUva16jzQ5ZkunQe0pKalrRjZ/DlZ+wgfgQxqjBw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.73.0.tgz", + "integrity": "sha512-N2BZFlWmVt2zjpiqPnfmIlj7tV/wfKSZCFF/laLAnJSTZjSEdc9JYSXW+KUV0FMUNDfpLCeAcI1xDMdGLdxFJg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/propagator-aws-xray": "^2.1.4", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "^8.10.155" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.76.0.tgz", + "integrity": "sha512-gg2QaDtWeFezRt2mAl9vBQ38y60tzUShKg1KA9uTgsMJimUHaBnB3X8kEH9Of8jKWT4DDoDcSA37gPcoEc0hiA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.66.0.tgz", + "integrity": "sha512-IqYQC1dav35NHlD5nYnpBXK8tI6KJ9/MIt8LYKFxwlhMIwfBCfavaklyP8NtVKoJ0WZKzX2v9Sh+xGK1XvSniQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.66.0.tgz", + "integrity": "sha512-4ksN7PfXLg7raDyXIjIrtxxzuuDlUx6Fh0s8VXojdl+nn2o0xkYa9jrq1f9Bfqhm+Sce3GOa1RjE3ycvAwk6Xw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.64.0.tgz", + "integrity": "sha512-D1Tpom3BpY8g29FFOEQ2FZioVFjyXwXHsh3BOn2BHcg7Taipg+yc+DPGUwvdR4WZrKnNMAG/+FBXNa0S0KhJYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.37.0.tgz", + "integrity": "sha512-ezhn6D0DSUZkwLBGdfnr+PAENvB92AhbEH/dkcSLYB8dbQiw/NQ8y19jOn3E6MZTt0+FD1YyHtrJS2skDO4nDQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.38.0.tgz", + "integrity": "sha512-OmOVadK0m7sdlvMwbt1gb2iVUCyvVNDo3x5JLGgnKggLTJBgTcQxgMl3pAhFAMzWGo9URuMxuh3Bphy9Pb9nZw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.64.0.tgz", + "integrity": "sha512-La6s9SdKgojZQVFD7AclQBYe3WioVe6zicJswM3QPPPHpCufJYnw8rO/G9o2Yl/OUeS7PYpzwHh4N6lexzbEcA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.69.0.tgz", + "integrity": "sha512-91pHMujQgDyhEQrdg8RriMBrRZ/qPaJ0Y2dopQ6lHjW5YjoeytWi8ruM//T6f5o0D95hnqRlv79Pel1lGPqaYg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.40.0.tgz", + "integrity": "sha512-p39axaaYVKhnl5l4M+1aiXmxrAG2HuTti7DHxs2jDJRst828y5iwqUZLC1UWIKIhW9FfdV5gogXg+nRRhSc0EA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.64.0.tgz", + "integrity": "sha512-wM939j8Ox5BBHoA0r/p9etdpyS3GcUf/sfrUx1dtZdqGKU4ZcBxLyPD8QntwFkSI9PHql+rRejTCA6Btktz8Kg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.69.0.tgz", + "integrity": "sha512-vyKzuiBoEulV1FjMSe4iiuwZedt+nNAuaSVOh/3WxjDIuGQ9WsH+0ohd9snhIY6guAjYOGjqvTiYLs6K+OTQQg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.221.0.tgz", + "integrity": "sha512-1U45172SiPWG1MPfrgLItIuXZO/RfJqt5sxsrdlKN1NRV0pUtv7lbpgB1nShwP/SGSKaAFkovbVg3a1hfy3mpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.67.0.tgz", + "integrity": "sha512-cIXIN4vZXm6aI4yz+4oUIRnkiAxCIpONrMhnGTI+ILKKEsIXP8Uselfr9663+TnisrgRLB7kKp+SoOuGJRHGtw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-host-metrics": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.4.0.tgz", + "integrity": "sha512-jnFyX2sTn2B+9mjsL3qgAHzpxdaia4/FV2GSlf9QrpaiMi6O0M0lA9JZTsf4FTvuOwrIngCk+MeVXjsxgBXXyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "systeminformation": "^5.31.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.221.0.tgz", + "integrity": "sha512-oIP91CPIANuYr09tGFElPFKAh6JUar+awJf1kBRYlaeo9b0gDwZHEB2zBfFlvdNFHm0wAVutMZODVi5smKT30g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.69.0.tgz", + "integrity": "sha512-I9sZtxXWZ1tRXtRNTEVxpokGtXy6RL1SZhtPVh7zxH78t8ar71V5Dx4bnQiUjKTDzItpC73krD8c0/cEWA9oLg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.30.0.tgz", + "integrity": "sha512-/p/D4etxJpJGB0VrS+kqF8WfVAMFWf5ybhY0mjzIEd/d/T68+nxTWJaI5MJXFiyOnBUFuMlun12VJt+QJDCSZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.65.0.tgz", + "integrity": "sha512-rJTT12VlDnL6wOWfxnBvkTUIzW2ju+7nqlToMy1tlqL0j6ohlVPxryMCS5h6UqE3CFpq7HtHqVObQHZgrA37KA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.69.0.tgz", + "integrity": "sha512-fxuA8jFOdqQzJV9Sitd0dk+zns7RQCFe19ia3LHex5oLiQPaaQovBv37jndX/zAZw6EBORATePHE8OQUwraPCQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.65.0.tgz", + "integrity": "sha512-s2KisLZ82iDvCF2QbsV1k1wrz3DMSBP9OiMfmNn5oSyaNT7jcNphR4uxr7WjwH+ssucuvhKWqKzJ2vdx7KRMVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.64.0.tgz", + "integrity": "sha512-ek34tp7Qjci4CLahXybJ3aaixU1d2j28X5JSXSXbp6/rIiFGjMCAXJWw3FeiYmA82D/gV/wzPhL7r7m/p4gUzw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.74.0.tgz", + "integrity": "sha512-GRnHu69YLQUYgguuYkKi6wpizMY4r7gLC08rSq8cg41p6t7+1YIy5nXoGC61NA7KCZUE9jbcDnzd45i32IblZg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.67.0.tgz", + "integrity": "sha512-iEBgNrychD36qI16X/V8WZb2JabjQPE+pyrkLU320ApZyhGVsYU9LH3Nqt4Mkg1nFsa9qWhRQrCfhoANCgT6EA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.67.0.tgz", + "integrity": "sha512-G4aRrVKcd2Aodqi7WzRZ3LQJNKrM8BpsXEVMHrOb9s8Lg0jZXNvlLY0NWL1yVJrjnqI/unwEsp2aVHkVImPMeA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.67.0.tgz", + "integrity": "sha512-AmviR7l0xMxhC83scY3u+NkkT6blhD/xK9tPi9nYtjNG1gwPtMgZjYOa3f9lGOwpXs/EwN7wiyAgxiO4KTcENA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.42.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.67.0.tgz", + "integrity": "sha512-lXb7pjobd2i/9Gmihf9wrOM0MgnDYBxOJq7uWEhZOqULodNFLyPCRUnWxSIbPQdUzlU36QtHjuAeaEBUJnqXkA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.65.0.tgz", + "integrity": "sha512-W82H8UvaSrWynpI510CNJbq2Aq6L4/zuR/dAvoCZVYeRiChi0LHMI8i3rPe3Tmau2WBwE0jimaWgOs0GCfIHbQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-openai": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.19.0.tgz", + "integrity": "sha512-zHz7m/aUMDyAap7UMzaaxDkbmxEyUOfMBfh+7KICNwTBmIOypMnuUWwXWAfIJLgl+2dnZlNzxZRknULeS4YIhg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-oracledb": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.46.0.tgz", + "integrity": "sha512-nqxQvbp7HvsVPyDdgiZADPQX6B6ZUtLfm+XPuJHtQ3anxIcqU6qhJlrDIEM2LrZMShqf7bs84RHfDt2rgsp7hg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@types/oracledb": "6.5.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.73.0.tgz", + "integrity": "sha512-yf3tBVwLHB9cZNNPSToNrthx36ouPe4FctFxy7ya6vSJ6gaiKjNfA/IgFFeuBpZflEQhy6aesPqzZo8ZjFkvNg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.42.0", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.67.0.tgz", + "integrity": "sha512-Hb6phi2x1bq23OIiesj4imQvXs9Y5MMLtpgXH9hOuMm9LpBYz2cxPNgpgZ/XATuRclP1eRPoSl399o5XKwfoIA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.69.0.tgz", + "integrity": "sha512-lyCIEW89cYhMwaUSMBzsKHdwH2wOoqmuwXOARJneo9UL55govLIUCbYYAJ457oM8kdKADymlY4+SUW0DKQeIHw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.66.0.tgz", + "integrity": "sha512-9zbnL0ML2jFgJmmG1XPQTqwopCogC8eAtUQ0SXvYb+Ux2yuOBOvSg9XvRk/hcQf6WsRMll47cELCMC+IaI9I+g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.65.0.tgz", + "integrity": "sha512-ti9tDLFLhLoev5U/cGeQpFTlOjFFwrFbieZmwTFql9z8EijXDzFE7a3+3mgnxGl9CZR0luKCwFD/o51NPU+Ngg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.34.0.tgz", + "integrity": "sha512-Yb3PcmuK/iIOWY49GSEcSGl7fR4r6UqhZnuy5EWvFaqKqqs9SYnQeyQUEAbnBnSougRpwFBDbdN1SSGcvMhbtQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.68.0.tgz", + "integrity": "sha512-Bhd0KApVBYV4WQMZZbKRYfvev7SudvCtSn6b36uyUbKowOMEoGpnVoIvm0rlrrBR0KmkcV3Y37SngCGUrTm3lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.40.0.tgz", + "integrity": "sha512-zTNNxs+KUJf1J+lHzeTDxAIZdVJYvQ8mvGUfyiWcVFgVdl7+4XV+wOBMSd1tZcRRlopfcVODDCOVMx/N7+zvcA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.31.0.tgz", + "integrity": "sha512-qunCfgSFV+bjRdAYkWIjVX38jIN/Xj80CiERXXdzYAmdigCFvJPB3AY3j43bjJlkhgbJJSCGdbvQUSut8QIiyQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.65.0.tgz", + "integrity": "sha512-hWSPnS530deRa+ttzY+QiGmgsK7aQHpqxbQRm56yO1j4qnIXuYYHaoSJ8/4lLOEcXB+hZs0mAAJ6QiMl1aaiGA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.221.0.tgz", + "integrity": "sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-transformer": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.221.0.tgz", + "integrity": "sha512-rQDmNgyiGCTrescjnzH2ntVyUKVIq6I2UjuK8+stT/Xg0ZOT71FVJqwjFdspQl6Yol/Yqsut9bDo+ame8oTmDQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.221.0.tgz", + "integrity": "sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/propagator-aws-xray": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", + "integrity": "sha512-Yjvt2EjL+tfpkVOdKbhTPgpM4SIAez9nG6Q/QjQ3yfcJcjIWp59ph70SLfvmkSL6++3DCnuBG3iWcB18PwWavQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.10.0.tgz", + "integrity": "sha512-GnA5B24H+1w8BO21J0q+IWNB0z1v+AGbcquTdIt/dufibhnhgxaA8YKvz0I3akRZhB1jHT+/tlzK+qlAjEDybQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.10.0.tgz", + "integrity": "sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", + "integrity": "sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.36.0.tgz", + "integrity": "sha512-s75zJV1ShpYL5nk2cODfZY05Haw2hGxcfEFMu3ymvh2QU3HrhXaCW+rmNkhXhRrO8YophMFTyVdb7iCDleC/JQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.21.0.tgz", + "integrity": "sha512-Veavy+khoywR+Hv065SU5jucFTGTiW1KXo39CsJ+8wqdYYz8jiRJPnQ20Kd+X9HbV2+Abb0l5CrJIdxK1ZOqBg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.29.0.tgz", + "integrity": "sha512-lWm0vjjlQMoc4Xvvd+dW/OZWT/SI4w+cIN7kbm8KimIZCr1EpAyvyQ7WEOrGoBoXCpQCrsZ18uKTooDgiBCGIw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.12.tgz", + "integrity": "sha512-EJRFfIY26whY0w5RDxMRXlfBDgDS001JYMHuOVuDBBsRrV4MBqoVajR9B0L9Vy728+w/HNVnSQkpJFacFr+klg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.56.0.tgz", + "integrity": "sha512-H8yNeqTsuapbXs6MLZTtelfUCk+5D8jD3+KosCJaXOyx5gl3EWWvs70HbNXTUO4VYLxccySFYJYVCd8YMM0NJw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "gcp-metadata": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.221.0.tgz", + "integrity": "sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.10.0.tgz", + "integrity": "sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.221.0.tgz", + "integrity": "sha512-UbYuvtBrQQB5Prsh9KOKy4kxzexFxfMs5MkteHeWMoswsEB7kiNhyUVkAOFW/qsEzNHtrkgyghrD2ilZJa+5YA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/configuration": "0.221.0", + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-logs-otlp-http": "0.221.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.221.0", + "@opentelemetry/exporter-prometheus": "0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-trace-otlp-http": "0.221.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.221.0", + "@opentelemetry/exporter-zipkin": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/propagator-b3": "2.10.0", + "@opentelemetry/propagator-jaeger": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0", + "@opentelemetry/sdk-trace-node": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz", + "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.42.0.tgz", + "integrity": "sha512-nwUwUU+8O8a4bnLqk6CodWeegGMEANgC94KTAhXcpGWLrW/2/hek/0ajNbjXnSOoNuCX+nteUPs46HFHhou9Xw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "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, + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "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==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "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" + } + }, + "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==", + "devOptional": true, + "license": "Apache-2.0" + }, + "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==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "license": "BSD-3-Clause" + }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.18", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.18.tgz", + "integrity": "sha512-UyKIm0wTPw5BcY7Z2PkbK1Ma260um96LSBWXHrdSMe+ZV0EPMyDfAcUcjjm3qEiGST9OK/1TriekdPCZkn4Q3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.3.0", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/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" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sentry/core": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.69.0.tgz", + "integrity": "sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.69.0.tgz", + "integrity": "sha512-xEXA1YGIiTZbrW6MWV34uS6JGQuQg2ijTI0zed+FsJb9JZKPYel/GZK8Km26vfTVb+yCXFmWZNBesKegNcVdzg==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "@sentry/node-core": "10.69.0", + "@sentry/opentelemetry": "10.69.0", + "@sentry/server-utils": "10.69.0", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node-core": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.69.0.tgz", + "integrity": "sha512-IgArHczrZJxkgxoffHscj0NxQrG6kCazgmGQnlf3j58J1ec21YaUu8Tu+7G4Lo5tCiW3teQnwlKW1ttMXSqWRw==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "@sentry/opentelemetry": "10.69.0", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/core": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + } + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/api-logs": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", + "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.220.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@sentry/opentelemetry": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.69.0.tgz", + "integrity": "sha512-3FyWV6YcEJuvLrlaKGE1dHXCI+1YO0a62w7PkwlRg8yp6K6YXkmdwu9GjqaYD+Ju4tm7uC7mHIsGFQMm0M7pqQ==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + } + }, + "node_modules/@sentry/server-utils": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.69.0.tgz", + "integrity": "sha512-0MwHrA8+nNvMIsqf8m3cXwCBlUjr6AS7N6CZvHJtY1DkqEvQqEbD5VIrhzEyHN/KMZIgQ8XeDCQRhjnXFQGRhg==", + "license": "MIT", + "dependencies": { + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", + "@apm-js-collab/tracing-hooks": "^0.13.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "meriyah": "^6.1.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@simple-libs/child-process-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", + "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@sinclair/typebox" + "node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "node_modules/@stellar/js-xdr": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", + "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==" + }, + "node_modules/@stellar/stellar-base": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.1.tgz", + "integrity": "sha512-gOBSOFDepihslcInlqnxKZdIW9dMUO1tpOm3AtJR33K2OvpXG6SaVHCzAmCFArcCqI9zXTEiSoh70T48TmiHJA==", + "deprecated": "This package is now rolled into @stellar/stellar-sdk. Please use @stellar/stellar-sdk to continue receiving updates and support.", + "dependencies": { + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "buffer": "^6.0.3", + "sha.js": "^2.3.6", + "tweetnacl": "^1.0.3" + }, + "optionalDependencies": { + "sodium-native": "^4.1.1" + } + }, + "node_modules/@stellar/stellar-sdk": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-12.3.0.tgz", + "integrity": "sha512-F2DYFop/M5ffXF0lvV5Ezjk+VWNKg0QDX8gNhwehVU3y5LYA3WAY6VcCarMGPaG9Wdgoeh1IXXzOautpqpsltw==", + "dependencies": { + "@stellar/stellar-base": "^12.1.1", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "eventsource": "^2.0.2", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + } + }, + "node_modules/@stryker-mutator/api": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-8.7.1.tgz", + "integrity": "sha512-56vxcVxIfW0jxJhr7HB9Zx6Xr5/M95RG9MUK1DtbQhlmQesjpfBBsrPLOPzBJaITPH/vOYykuJ69vgSAMccQyw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-metrics": "3.3.0", + "mutation-testing-report-schema": "3.3.0", + "tslib": "~2.7.0", + "typed-inject": "~4.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@stryker-mutator/api/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/core": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-8.7.1.tgz", + "integrity": "sha512-r2AwhHWkHq6yEe5U8mAzPSWewULbv9YMabLHRzLjZnjj+Ipxtg+Zo22rrUc2Zl7mnYvb9w34bdlEzGz6MKgX2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@inquirer/prompts": "^6.0.0", + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/instrumenter": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "ajv": "~8.17.1", + "chalk": "~5.3.0", + "commander": "~12.1.0", + "diff-match-patch": "1.0.5", + "emoji-regex": "~10.4.0", + "execa": "~9.4.0", + "file-url": "~4.0.0", + "lodash.groupby": "~4.6.0", + "minimatch": "~9.0.5", + "mutation-testing-elements": "3.4.0", + "mutation-testing-metrics": "3.3.0", + "mutation-testing-report-schema": "3.3.0", + "npm-run-path": "~6.0.0", + "progress": "~2.0.3", + "rxjs": "~7.8.1", + "semver": "^7.6.3", + "source-map": "~0.7.4", + "tree-kill": "~1.2.2", + "tslib": "2.7.0", + "typed-inject": "~4.0.0", + "typed-rest-client": "~2.1.0" + }, + "bin": { + "stryker": "bin/stryker.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/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, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@stryker-mutator/core/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@stryker-mutator/core/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@stryker-mutator/core/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/execa": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.4.1.tgz", + "integrity": "sha512-5eo/BRqZm3GYce+1jqX/tJ7duA2AnE39i88fuedNFUV8XxGxUpF3aWkBRfbUcjV49gCkvS/pzc0YrCPhaIewdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@stryker-mutator/core/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/instrumenter": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-8.7.1.tgz", + "integrity": "sha512-HSq4VHXesQCMR3hr6bn41DAeJ0yuP2vp9KSnls2TySNawFVWOCaKXpBX29Skj3zJQh7dnm7HuQg8HuXvJK15oA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "~7.25.2", + "@babel/generator": "~7.25.0", + "@babel/parser": "~7.25.0", + "@babel/plugin-proposal-decorators": "~7.24.7", + "@babel/plugin-proposal-explicit-resource-management": "^7.24.7", + "@babel/preset-typescript": "~7.24.7", + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "angular-html-parser": "~6.0.2", + "semver": "~7.6.3", + "weapon-regex": "~1.3.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/core": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.9.tgz", + "integrity": "sha512-WYvQviPw+Qyib0v92AwNIrdLISTp7RfDkM7bPqBvpbnhY4wq8HvHBZREVdYDXk98C8BkOIVnHAY3yvj7AVISxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helpers": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@stryker-mutator/instrumenter/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, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.9.tgz", + "integrity": "sha512-omlUGkr5EaoIJrhLf9CJ0TvjBRpd9+AXRG//0GEQ9THSo8wPiTlbpy1/Ow8ZTrbXpjd9FHXfbFQx32I04ht0FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", + "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@stryker-mutator/jest-runner": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/jest-runner/-/jest-runner-8.7.1.tgz", + "integrity": "sha512-507jgu9E0yNDwMN56p4J+iFI77+rPDLDV91qYGbBI6fvy5c7rBQ63l64FtAFMTG75f4gCZ6C/Pq3nXTQx6PMjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "semver": "~7.6.3", + "tslib": "~2.7.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@stryker-mutator/core": "~8.7.0" + } + }, + "node_modules/@stryker-mutator/jest-runner/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@stryker-mutator/jest-runner/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-8.7.1.tgz", + "integrity": "sha512-Oj/sIHZI1GLfGOHKnud4Gw0ZRufm7ONoQYNnhcaAYEXTWraYVcV7mue/th8fZComTHvDPA8Ge8U16FvWYEb8dg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.162", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz", + "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==", + "license": "MIT" + }, + "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, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "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, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "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, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", + "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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 + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@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==" + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "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, + "license": "MIT" + }, + "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 + }, + "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, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "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, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/luxon": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.5.tgz", + "integrity": "sha512-jJ41Q4z6ZVO260MNDdHfW7+7a5iMiX8Mr6ZJHcmgrvhZha6dz5704o/lF2kKl6URjH6ivEL97w9xS/MgpJEphg==", + "license": "MIT" + }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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 + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz", + "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node/node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "license": "MIT" + }, + "node_modules/@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parquetjs": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/parquetjs/-/parquetjs-0.10.6.tgz", + "integrity": "sha512-ZCsD6j97YD0mGU8/VnVs3NjORXa7zeHvqlpJpCqy4jU8a1O21dalL+MFn9QNbdEfy8rszR1N7NHeT7/LdtHf+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-int64": "*" + } + }, + "node_modules/@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, + "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, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "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 + }, + "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, + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.1.tgz", + "integrity": "sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==", + "dev": true, + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, + "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==" + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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, + "dependencies": { + "@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 + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", + "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/type-utils": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "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.70.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", + "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "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" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", + "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", + "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.70.1", + "@typescript-eslint/types": "^8.70.1", + "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" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", + "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", + "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "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" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", + "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.70.1", + "@typescript-eslint/tsconfig-utils": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "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": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "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" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/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, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/type-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, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", + "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1" + }, + "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" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", + "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", + "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.70.1", + "@typescript-eslint/tsconfig-utils": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "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": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.1", + "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" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/utils/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, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/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, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vortex/solver-sdk": { + "resolved": "packages/solver-sdk", + "link": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/angular-html-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-6.0.2.tgz", + "integrity": "sha512-8+sH1TwYxv8XsQes1psxTHMtWRBbJFA/jY0ThqpT4AgCiRdhTtRxru0vlBfyRJpL9CHd3G06k871bR2vyqaM6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "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, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "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==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/argue-cli": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", + "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "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, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bare-addon-resolve": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.1.tgz", + "integrity": "sha512-F/SD2du8keuYSb4xipnGz5j2E6yhNdHA8ZVxtHae6h2uOrpBIjjbhXvjzKZbr5XUOzqBzh/i8GVFycj2DlFQIA==", + "optional": true, + "dependencies": { + "bare-module-resolve": "^1.10.0", + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-module-resolve": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.4.tgz", + "integrity": "sha512-xcfgg2u7HqgJiBmah71O9vvdFAgHCvkqC/WSC2O7Bbgosoc1eC/BWe/6IDJ4OsfKlkxuvC/TDWXC+oH5yeW8mA==", + "optional": true, + "dependencies": { + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-semver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz", + "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==", + "optional": true + }, + "node_modules/base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.5.tgz", + "integrity": "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g==", + "license": "MIT", + "optional": true + }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "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" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bullmq": { + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-6.3.9.tgz", + "integrity": "sha512-wBGk4tHZRFcQayZlC5DPRxg2EFfCqhgkJq0uQ8EvufMgh2hYmPeH8tK+1U4ffimNmnJzTUhwwW1PNUIQMhvSpQ==", + "license": "MIT", + "dependencies": { + "cron-parser": "5.10.1", + "msgpackr": "2.1.0", + "node-abort-controller": "3.1.1", + "semver": "7.8.5", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=14.17.0" + }, + "peerDependencies": { + "bullmq-otel": ">=2.0.0", + "ioredis": ">=5.0.0", + "pg": ">=8.0.0", + "redis": ">=5.0.0" + }, + "peerDependenciesMeta": { + "bullmq-otel": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "pg": { + "optional": true + }, + "redis": { + "optional": true + } + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.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==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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, + "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" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "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, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.15.1.tgz", + "integrity": "sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "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, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "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 + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", + "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "dev": true, + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "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, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", + "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/conventional-commits-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=22" + } + }, + "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 + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, + "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 + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/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/cron": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.4.0.tgz", + "integrity": "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.7.0", + "luxon": "~3.7.0" + }, + "engines": { + "node": ">=18.x" + }, + "funding": { + "type": "ko-fi", + "url": "https://ko-fi.com/intcreator" + } + }, + "node_modules/cron-parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-5.10.1.tgz", + "integrity": "sha512-pKRrRagItwk9rGIStcUyMxFX34x56zoX3KDf9HJ4ttwkXIK1qxK63EvXvEDmlxI9AdPJ+Y7TEKRftRlW5eSS7A==", + "license": "MIT", + "dependencies": { + "luxon": "^3.7.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "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 + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "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==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-6.0.1.tgz", + "integrity": "sha512-UaaM14yby8U3k02ihS1Bmj5Kz2d7CCQM1scxpgs4Mhkq8F1wR2gl3+Ts4h5Ne4Mnt7M9m4Dw7jsuMr3+xO4vZA==", + "license": "Apache-2.0", + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.12.18" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true + }, + "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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "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==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==" + }, + "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==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", + "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-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": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/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/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 + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/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==", + "dev": true + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-check": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.10.2.tgz", + "integrity": "sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/fast-check/node_modules/pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "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 + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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/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 + }, + "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 + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/file-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-4.0.0.tgz", + "integrity": "sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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==", + "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" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "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, + "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" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/gaxios/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gaxios/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "7.1.3", + "google-logging-utils": "1.1.3", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/glob/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/glob/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/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/global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "6.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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 + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", + "engines": { + "node": ">=16.0.0" + } + }, + "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 + }, + "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==", + "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" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "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, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.2.tgz", + "integrity": "sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==", + "license": "Apache-2.0", + "dependencies": { + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "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, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", + "license": "BSD-3-Clause" + }, + "node_modules/ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=20.0.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==", + "engines": { + "node": ">= 0.10" + } + }, + "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 + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/joi": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", + "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true, + "license": "MIT" + }, + "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 + }, + "node_modules/js-yaml": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz", + "integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==", + "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.mjs" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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 + }, + "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 + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "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 + }, + "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, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.9.tgz", + "integrity": "sha512-VNS5vWMM7r0P66BYv+TQJATxExEgLxN+34hfHDVhDkUsGAE4cRg0shCNSLTXNKm7nIUscC7AfB51TjxEeF7msQ==" + }, + "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 + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "dev": true, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/lzo": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/lzo/-/lzo-0.4.11.tgz", + "integrity": "sha512-apQHNoW2Alg72FMqaC/7pn03I7umdgSVFt2KRkCXXils4Z9u3QBh1uOtl2O5WmZIDLd9g6Lu4lIdOLmiSTFVCQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bindings": "~1.2.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "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/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/meriyah": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz", + "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==", + "license": "ISC", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/msgpackr": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.0.tgz", + "integrity": "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.4" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/multer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mutation-testing-elements": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-3.4.0.tgz", + "integrity": "sha512-zFJtGlobq+Fyq95JoJj0iqrmwLSLQyIJuDATLwFMDSJCxpGN8kHCA6S4LoLJnkSL6bg4Aqultp8OBSMxGbW3EA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mutation-testing-metrics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-3.3.0.tgz", + "integrity": "sha512-vZEJ84SpK3Rwyk7k28SORS5o6ZDtehwifLPH6fQULrozJqlz2Nj8vi52+CjA+aMZCyyKB+9eYUh1HtiWVo4o/A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-report-schema": "3.3.0" + } + }, + "node_modules/mutation-testing-report-schema": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-3.3.0.tgz", + "integrity": "sha512-DF56q0sb0GYzxYUYNdzlfQzyE5oJBEasz8zL76bt3OFJU8q4iHSdUDdihPWWJD+4JLxSs3neM/R968zYdy0SWQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "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 + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "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==" + }, + "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, + "engines": { + "node": ">=18" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-stream": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/object-stream/-/object-stream-0.0.1.tgz", + "integrity": "sha512-+NPJnRvX9RDMRY9mOWOo/NDppBjbZhXirNNSu2IBnuNboClC9h1ZGHXgHBLDbJMHsxeJDq922aVmG5xs24a/cA==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "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, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/openapi-typescript/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/openapi-typescript/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, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ox": { + "version": "0.14.45", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.45.tgz", + "integrity": "sha512-jpsQ+p0JZh9mKCxxzxz0d5qFHZZg2/O9xW18IpEC/dNXXPVqmDJ0c//9RMz0CILkbSGPA42+cbU0fC/ghwJ03w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parquetjs": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/parquetjs/-/parquetjs-0.11.2.tgz", + "integrity": "sha512-Y6FOc3Oi2AxY4TzJPz7fhICCR8tQNL3p+2xGQoUAMbmlJBR7+JJmMrwuyMjIpDiM7G8Wj/8oqOH4UDUmu4I5ZA==", + "license": "MIT", + "dependencies": { + "brotli": "^1.3.0", + "bson": "^1.0.4", + "int53": "^0.2.4", + "object-stream": "0.0.1", + "snappyjs": "^0.6.0", + "thrift": "^0.11.0", + "varint": "^5.0.0" + }, + "engines": { + "node": ">=7.6" + }, + "optionalDependencies": { + "lzo": "^0.4.0" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pg": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", + "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.6.tgz", + "integrity": "sha512-lqIfH7bdgsxHAY/ZnUOwm+aCFKrsHBDhSFuk9O0B9uCqJAIkrKTo/+LQqLPLUS4e04+jCmQVikxE3QipH5chPw==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "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 + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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, + "engines": { + "node": ">= 6" + } + }, + "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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "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/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prom-client": { + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", + "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.4.0", + "tdigest": "^0.1.1" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, + "node_modules/protobufjs": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "engines": { + "node": ">=10" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react-is-18": { + "name": "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 + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "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-info": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redis-info/-/redis-info-3.1.0.tgz", + "integrity": "sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.11" + } + }, + "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==" + }, + "node_modules/require-addon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", + "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", + "optional": true, + "dependencies": { + "bare-addon-resolve": "^1.3.0" + }, + "engines": { + "bare": ">=1.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, + "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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/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==", + "dev": true + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "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", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/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 + }, + "node_modules/semifies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", + "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==", + "license": "Apache-2.0" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "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" + } + }, + "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==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snappyjs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.6.1.tgz", + "integrity": "sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==", + "license": "MIT" + }, + "node_modules/sodium-native": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", + "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", + "optional": true, + "dependencies": { + "require-addon": "^1.1.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.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, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "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, + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.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, + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.32.8", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.8.tgz", + "integrity": "sha512-dgMdWXIgnI4zX4OPhKEdWnlDODbgm8W3AX0Ivn/BBqcUh6xZsBxhZMnvk6DJyRz1BTrj8dPxtarmEGgkz30oyA==", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/systeminformation": { + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.1.tgz", + "integrity": "sha512-DEN6ICHk3Tk0Uf/hrAHh7xlt7iL5CJFBtPZinA0H62DrGG/KPKqq/Nzj6lCXPS4Ay/sf/14zNnk9LpqKzBIc+w==", + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "license": "MIT", + "dependencies": { + "bintrees": "1.0.2" + } + }, + "node_modules/terser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "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", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thrift": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/thrift/-/thrift-0.11.0.tgz", + "integrity": "sha512-UpsBhOC45a45TpeHOXE4wwYwL8uD2apbHTbtBvkwtUU4dNwCjC7DpQTjw2Q6eIdfNtw+dKthdwq94uLXTJPfFw==", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0", + "q": "^1.5.0", + "ws": ">= 2.2.3" + }, + "engines": { + "node": ">= 4.1.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity" + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/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==", + "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" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "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, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "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, + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^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 + } + } + }, + "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/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsx": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "dev": true, + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-inject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-4.0.0.tgz", + "integrity": "sha512-OuBL3G8CJlS/kjbGV/cN8Ni32+ktyyi6ADDZpKvksbX0fYBV5WcukhRCYa7WqLce7dY/Br2dwtmJ9diiadLFpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/typed-rest-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", + "integrity": "sha512-Nel9aPbgSzRxfs1+4GoSB4wexCF+4Axlk7OSGVQCMa+4fWcyxIsN/YNmkp0xTT2iQzMD98h8yFLav/cNaULmRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "des.js": "^1.1.0", + "js-md4": "^0.3.2", + "qs": "^6.10.3", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "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/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "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, + "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" + } + }, + "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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/valibot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.5.0.tgz", + "integrity": "sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/viem": { + "version": "2.56.9", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.56.9.tgz", + "integrity": "sha512-UC1G0Qz+RbPKqwl6yRrp3dH6ltrOmUcFJMTcOyE1Mg0nO5lxrEBTONh/8OFWPnALGuN/O7LkNcz/IsSj83Svqw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.14.45", + "ws": "8.21.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weapon-regex": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-1.3.6.tgz", + "integrity": "sha512-wsf1m1jmMrso5nhwVFJJHSubEBf3+pereGd7+nBKtYJ18KoB/PWJOHS3WRkwS04VrOU0iJr2bZU+l1QaTJ+9nA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack": { + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.1", + "mime-db": "^1.54.0", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", + "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "packages/solver-sdk": { + "name": "@vortex/solver-sdk", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "viem": "^2.44.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@stellar/stellar-sdk": ">=12.0.0" + } + } + }, + "dependencies": { + "@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==" + }, + "@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@angular-devkit/core": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.27.tgz", + "integrity": "sha512-3amNzoCVSKd7ah6l6lBQL4onwwJvqvam7FMoQBILrxtW5LB5ezh8gMSPuA4zJjKjoRzf9uoWdlzqv/84I52xZA==", + "dev": true, + "requires": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "dependencies": { + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } + } + }, + "@angular-devkit/schematics": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.27.tgz", + "integrity": "sha512-/PZmyAlb2NGWPikRRuiWLdfHQd8Wrx6lX4HqvTcaDhlU43M3T0ud4PH2T3QDp7BzHYY92xtD8iPxX2asg67G1A==", + "dev": true, + "requires": { + "@angular-devkit/core": "19.2.27", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "dependencies": { + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } + } + }, + "@angular-devkit/schematics-cli": { + "version": "19.2.27", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.27.tgz", + "integrity": "sha512-wHYH6SVXVykhLzovUHtYor3Nl4SpIiITi7r9DQDaKYUD4hpRBx25W6N9eGuakT9Vd5tV/x6wmvQFWQZQwFB7eA==", + "dev": true, + "requires": { + "@angular-devkit/core": "19.2.27", + "@angular-devkit/schematics": "19.2.27", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "dependencies": { + "@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "requires": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + } + } + } + }, + "@apm-js-collab/code-transformer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", + "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", + "requires": { + "@types/estree": "^1.0.8", + "astring": "^1.9.0", + "esquery": "^1.7.0", + "meriyah": "^6.1.4", + "semifies": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@apm-js-collab/code-transformer-bundler-plugins": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.3.tgz", + "integrity": "sha512-qNbPwuMZ8f5ZuGj/ttPeB7a6C/S1bB6tNYaEL5vNiRKydSAxa4AU0gxCWgaP4fVju+AuwhcumSFjrEcGF9Dv7Q==", + "requires": { + "@apm-js-collab/code-transformer": "^0.18.0", + "es-module-lexer": "^2.1.0", + "magic-string": "^0.30.21", + "module-details-from-path": "^1.0.4" + }, + "dependencies": { + "magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + } + } + }, + "@apm-js-collab/tracing-hooks": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", + "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", + "requires": { + "@apm-js-collab/code-transformer": "^0.18.0", + "debug": "^4.4.1", + "module-details-from-path": "^1.0.4" + } + }, + "@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": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + } + }, + "@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 + }, + "@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": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "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 + } + } + }, + "@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": { + "@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" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "requires": { + "@babel/types": "^7.29.7" + } + }, + "@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": { + "@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 + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "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 + } + } + }, + "@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 + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + } + }, + "@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": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + } + }, + "@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": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "requires": { + "@babel/types": "^7.29.7" + } + }, + "@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 + }, + "@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "requires": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + } + }, + "@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 + }, + "@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 + }, + "@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 + }, + "@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "requires": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + } + }, + "@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": { + "@babel/types": "^7.29.7" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", + "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7" + } + }, + "@babel/plugin-proposal-explicit-resource-management": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-explicit-resource-management/-/plugin-proposal-explicit-resource-management-7.27.4.tgz", + "integrity": "sha512-1SwtCDdZWQvUU1i7wt/ihP7W38WjC3CSTOHAl+Xnbze8+bbMNjRvRQydnj0k9J1jPqCAZctBFp6NHJXkrVVmEA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.3" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.29.7" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.29.7" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.29.7" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@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": { + "@babel/helper-plugin-utils": "^7.29.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + } + }, + "@babel/preset-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", + "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.24.7" + } + }, + "@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": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + } + }, + "@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": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + } + }, + "@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": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + } + }, + "@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": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==" + }, + "@bull-board/api": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-9.10.1.tgz", + "integrity": "sha512-yaFVInFvQAKHQwW7xxdoWQaC2xW1f8JOAZ5k6Zl8GQBTnlhm6UmeRQUr2Gb6wMgfl5v61p2eSupPow2CEvz0ng==", + "requires": { + "redis-info": "^3.1.0", + "valibot": "^1.5.0" + } + }, + "@bull-board/express": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-9.10.1.tgz", + "integrity": "sha512-sUXrFAwbllDNGyieB51Xr7bFa+f7uPdWWPMcArROYlAXsZqrG0HyRPf81bpCX8ZnBGTUcbsEq9jSdRpEfbqm7g==", + "requires": { + "@bull-board/api": "9.10.1", + "@bull-board/ui": "9.10.1", + "ejs": "^6.0.1", + "express": "^5.2.1" + } + }, + "@bull-board/ui": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-9.10.1.tgz", + "integrity": "sha512-1E0zbLaljf+Zhx3nDUotzsG2Dz7/4i72nWCxKkFwxfz65MKXfObo2mXWMuIDoXAeWSa3sm7hlPj3h3scts4DaQ==", + "requires": { + "@bull-board/api": "9.10.1" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, + "@commitlint/cli": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.3.tgz", + "integrity": "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ==", + "dev": true, + "requires": { + "@commitlint/config-conventional": "^21.2.3", + "@commitlint/format": "^21.2.3", + "@commitlint/lint": "^21.2.3", + "@commitlint/load": "^21.2.3", + "@commitlint/read": "^21.2.3", + "@commitlint/types": "^21.2.3", + "tinyexec": "^1.0.0", + "yargs": "^18.0.0" + }, + "dependencies": { + "@commitlint/config-conventional": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", + "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-conventionalcommits": "^10.0.0" + } + }, + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + }, + "ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "requires": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "dependencies": { + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + } + } + }, + "conventional-changelog-conventionalcommits": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "dev": true, + "requires": { + "@conventional-changelog/template": "^1.4.0" + } + }, + "emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true + }, + "string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "requires": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + } + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "requires": { + "ansi-regex": "^6.2.2" + } + }, + "wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "requires": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "dependencies": { + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + } + } + }, + "yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "requires": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + } + }, + "yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true + } + } + }, + "@commitlint/config-conventional": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", + "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-conventionalcommits": "^10.0.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + }, + "conventional-commits-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "dev": true, + "requires": { + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" + } + } + } + }, + "@commitlint/config-validator": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.3.tgz", + "integrity": "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "ajv": "^8.11.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/ensure": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.3.tgz", + "integrity": "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "es-toolkit": "^1.46.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/execute-rule": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", + "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", + "dev": true + }, + "@commitlint/format": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.3.tgz", + "integrity": "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "picocolors": "^1.1.1" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/is-ignored": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.3.tgz", + "integrity": "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "semver": "^7.6.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/lint": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.3.tgz", + "integrity": "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg==", + "dev": true, + "requires": { + "@commitlint/is-ignored": "^21.2.3", + "@commitlint/parse": "^21.2.3", + "@commitlint/rules": "^21.2.3", + "@commitlint/types": "^21.2.3" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/load": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.3.tgz", + "integrity": "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^21.2.3", + "@commitlint/execute-rule": "^21.0.1", + "@commitlint/resolve-extends": "^21.2.3", + "@commitlint/types": "^21.2.3", + "cosmiconfig": "^9.0.1", + "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", + "is-plain-obj": "^4.1.0", + "picocolors": "^1.1.1" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + }, + "cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "requires": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + } + }, + "cosmiconfig-typescript-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", + "dev": true, + "requires": { + "jiti": "2.6.1" + } + }, + "js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@commitlint/message": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", + "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", + "dev": true + }, + "@commitlint/parse": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.3.tgz", + "integrity": "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w==", + "dev": true, + "requires": { + "@commitlint/types": "^21.2.3", + "conventional-changelog-angular": "^9.0.0", + "conventional-commits-parser": "^7.0.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/read": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.3.tgz", + "integrity": "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og==", + "dev": true, + "requires": { + "@commitlint/top-level": "^21.2.0", + "@commitlint/types": "^21.2.3", + "@conventional-changelog/git-client": "^3.0.0", + "tinyexec": "^1.0.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/resolve-extends": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.3.tgz", + "integrity": "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA==", + "dev": true, + "requires": { + "@commitlint/config-validator": "^21.2.3", + "@commitlint/types": "^21.2.3", + "es-toolkit": "^1.46.0", + "global-directory": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + }, + "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 + } + } + }, + "@commitlint/rules": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.3.tgz", + "integrity": "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g==", + "dev": true, + "requires": { + "@commitlint/ensure": "^21.2.3", + "@commitlint/message": "^21.2.0", + "@commitlint/to-lines": "^21.0.1", + "@commitlint/types": "^21.2.3" + }, + "dependencies": { + "@commitlint/types": { + "version": "21.2.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", + "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "dev": true, + "requires": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + } + } + } + }, + "@commitlint/to-lines": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", + "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", + "dev": true + }, + "@commitlint/top-level": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", + "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", + "dev": true, + "requires": { + "escalade": "^3.2.0" + } + }, + "@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", + "dev": true, + "requires": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "dependencies": { + "chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true + } + } + }, + "@conventional-changelog/git-client": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", + "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", + "dev": true, + "requires": { + "@simple-libs/child-process-utils": "^2.0.0", + "@simple-libs/stream-utils": "^2.0.0", + "semver": "^7.5.2" + } + }, + "@conventional-changelog/template": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", + "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", + "dev": true + }, + "@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "requires": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "dev": true, + "optional": true + }, + "@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": { + "eslint-visitor-keys": "^3.4.3" + } + }, + "@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 + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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" + } + }, + "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 + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true + }, + "@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "requires": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + } + }, + "@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "requires": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + } + }, + "@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==" + }, + "@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==" + }, + "@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==" + }, + "@hapi/tlds": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz", + "integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==" + }, + "@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "@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 + }, + "@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true + }, + "@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + } + }, + "@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + } + }, + "@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "requires": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + } + }, + "@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true + }, + "@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + } + }, + "@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + } + }, + "@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + } + }, + "@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "requires": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + } + }, + "@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "requires": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + } + }, + "@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "requires": {} + }, + "@ioredis/commands": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-2.0.0.tgz", + "integrity": "sha512-vrx0AE/T0h7cRZwfo1M39Cr+ZhZrkf0V8mQN75wucKCxCLD9l/VX6no3gFvrLqD1IlG/1LtzWovqEw3t0Vr9zg==" + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "requires": { + "ansi-regex": "^6.2.2" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@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": { + "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 + } + } + }, + "@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 + }, + "@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "requires": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + } + }, + "@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true + }, + "@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "requires": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + } + }, + "@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "requires": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + } + }, + "@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0" + } + }, + "@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + } + }, + "@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true + }, + "@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "requires": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + } + }, + "@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + } + }, + "@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.34.0" + } + }, + "@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + } + }, + "@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + } + }, + "@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "requires": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + } + }, + "@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "requires": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + } + }, + "@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "requires": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + } + }, + "@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": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@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": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@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 + }, + "@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@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==" + }, + "@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": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==" + }, + "@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==" + }, + "@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==" + }, + "@msgpack/msgpack": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz", + "integrity": "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==", + "dev": true + }, + "@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "optional": true + }, + "@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "optional": true, + "requires": { + "@tybys/wasm-util": "^0.10.3" + } + }, + "@nestjs/cli": { + "version": "11.0.24", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.24.tgz", + "integrity": "sha512-aIHxQLSYtXShifA3zwWIeznEsZnNa3Iz2QRykFj+sl9IcbERBHr5nH87FRgywM+He3NxoF5WazHfR8FsmVeWxw==", + "dev": true, + "requires": { + "@angular-devkit/core": "19.2.27", + "@angular-devkit/schematics": "19.2.27", + "@angular-devkit/schematics-cli": "19.2.27", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.6", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.106.2", + "webpack-node-externals": "3.0.0" + } + }, + "@nestjs/common": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.28.tgz", + "integrity": "sha512-bRImsxibie+AM7xjdwcrm/gr5YeacI65kSBNzTufa1Ib5iwziaY/lqMtRh9THq6pbV4e1HP9aI2ZxGUumnmaoQ==", + "requires": { + "file-type": "21.3.4", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + } + }, + "@nestjs/config": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.4.tgz", + "integrity": "sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==", + "requires": { + "dotenv": "17.4.1", + "dotenv-expand": "12.0.3", + "lodash": "4.18.1" + }, + "dependencies": { + "dotenv": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==" + } + } + }, + "@nestjs/core": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.28.tgz", + "integrity": "sha512-06m63xIRj8+l8uOeh/8LnYupGubkyu4f+bPKIadaSui6vK9KpXgoz7HveT1yOVLcEt0M0oCOEW5EuEXZkEmBBQ==", + "requires": { + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1", + "uid": "2.0.2" + } + }, + "@nestjs/event-emitter": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-12.0.1.tgz", + "integrity": "sha512-7MbpgNcxUcDlqcYY/qgZm1H7MfK3uGcRsd8lEjj9ZYvI9qTl6kdkkykpxZ6YjBRH3wlZK5dFLAei8h5qpsHzOw==", + "requires": { + "eventemitter2": "6.4.9" + } + }, + "@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==", + "requires": {} + }, + "@nestjs/platform-express": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.28.tgz", + "integrity": "sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==", + "requires": { + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.2.0", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1" + } + }, + "@nestjs/platform-ws": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/platform-ws/-/platform-ws-11.1.28.tgz", + "integrity": "sha512-anktDtSYoJEpG8zVvOKHhc2+W9jSX0c3CmnuYUICqgwtc2vC/Md9dBWbyxbB8iU5F5dWICC74MQrBxJMfnZh2g==", + "requires": { + "tslib": "2.8.1", + "ws": "8.21.0" + }, + "dependencies": { + "ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "requires": {} + } + } + }, + "@nestjs/schedule": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-12.0.2.tgz", + "integrity": "sha512-5iAvJEtk0njbfTrG4JQr+SXw8ZrqgDM1wJH9nUs6tVLCX5pWGd1LJ0a8b089UT1FDeDWaThRRfSLixFz9zztcA==", + "requires": { + "cron": "4.4.0" + } + }, + "@nestjs/schematics": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz", + "integrity": "sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==", + "dev": true, + "requires": { + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "comment-json": "5.0.0", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.24.tgz", + "integrity": "sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==", + "dev": true, + "requires": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + } + }, + "@angular-devkit/schematics": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", + "integrity": "sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==", + "dev": true, + "requires": { + "@angular-devkit/core": "19.2.24", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + } + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } + } + }, + "@nestjs/swagger": { + "version": "11.4.6", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.4.6.tgz", + "integrity": "sha512-Le136h2WC7HGsd70+WyK1qrm+Zq7kFxBLkYC1JgAVqNRCt8kNh7bMF7Qkn65D5j2t/aks0+VbWmUVlYIwPrs3A==", + "requires": { + "@microsoft/tsdoc": "0.16.0", + "@nestjs/mapped-types": "2.1.1", + "js-yaml": "5.2.1", + "lodash": "4.18.1", + "path-to-regexp": "8.4.2", + "swagger-ui-dist": "5.32.8" + } + }, + "@nestjs/testing": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-12.1.0.tgz", + "integrity": "sha512-OL6ar+p4BRy26YlOk6nqSchGShQajW0jaPSz2tvP7C3dLQcb26Wm0AG2IlvqIBoHuN3zQFMc5DoLAyiosPP4pw==", + "dev": true, + "requires": { + "tslib": "2.8.1" + } + }, + "@nestjs/throttler": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", + "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", + "requires": {} + }, + "@nestjs/websockets": { + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.28.tgz", + "integrity": "sha512-jeyclAURCJTN8S8lctDhfLdiJeDKjZmYWWLav653Fb9hl9c+zx5jPhavI8Xk5++R8u+lX9qzaRxtsjEoxTtjyw==", + "requires": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + } + }, + "@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==" + }, + "@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "requires": { + "@noble/hashes": "1.8.0" + } + }, + "@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==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@openfeature/core": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@openfeature/core/-/core-1.12.0.tgz", + "integrity": "sha512-7PCPzyd1OC19begz30+CRknFB0ChtyaZUhk7YWrk+Bov1fpVw0HYkTXtoxxsvPfDZB9P9WsT7uixN+Z8f9+bcA==" + }, + "@openfeature/server-sdk": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@openfeature/server-sdk/-/server-sdk-1.23.0.tgz", + "integrity": "sha512-JWeLvltJIV0AFgOfbw7hK9b9Rw/5wWq+RMhCIU9sJtAqarxr8hSPglG+JAGGhYdElAz3Qwe9W3ApWyPNkQEyqg==", + "requires": {} + }, + "@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==" + }, + "@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "requires": { + "@opentelemetry/api": "^1.3.0" + } + }, + "@opentelemetry/auto-instrumentations-node": { + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.79.0.tgz", + "integrity": "sha512-qL53aIjdw56sRDqz6LXD9h15vPTJgPpqv80rbsnRjzhuC9VqZ58fgk/lx0SdECJ2rcu8keeji5ZgjzJwiQZ0fg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/instrumentation-amqplib": "^0.68.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.73.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.76.0", + "@opentelemetry/instrumentation-bunyan": "^0.66.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.66.0", + "@opentelemetry/instrumentation-connect": "^0.64.0", + "@opentelemetry/instrumentation-cucumber": "^0.37.0", + "@opentelemetry/instrumentation-dataloader": "^0.38.0", + "@opentelemetry/instrumentation-dns": "^0.64.0", + "@opentelemetry/instrumentation-express": "^0.69.0", + "@opentelemetry/instrumentation-fs": "^0.40.0", + "@opentelemetry/instrumentation-generic-pool": "^0.64.0", + "@opentelemetry/instrumentation-graphql": "^0.69.0", + "@opentelemetry/instrumentation-grpc": "^0.221.0", + "@opentelemetry/instrumentation-hapi": "^0.67.0", + "@opentelemetry/instrumentation-host-metrics": "^0.4.0", + "@opentelemetry/instrumentation-http": "^0.221.0", + "@opentelemetry/instrumentation-ioredis": "^0.69.0", + "@opentelemetry/instrumentation-kafkajs": "^0.30.0", + "@opentelemetry/instrumentation-knex": "^0.65.0", + "@opentelemetry/instrumentation-koa": "^0.69.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.65.0", + "@opentelemetry/instrumentation-memcached": "^0.64.0", + "@opentelemetry/instrumentation-mongodb": "^0.74.0", + "@opentelemetry/instrumentation-mongoose": "^0.67.0", + "@opentelemetry/instrumentation-mysql": "^0.67.0", + "@opentelemetry/instrumentation-mysql2": "^0.67.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.67.0", + "@opentelemetry/instrumentation-net": "^0.65.0", + "@opentelemetry/instrumentation-openai": "^0.19.0", + "@opentelemetry/instrumentation-oracledb": "^0.46.0", + "@opentelemetry/instrumentation-pg": "^0.73.0", + "@opentelemetry/instrumentation-pino": "^0.67.0", + "@opentelemetry/instrumentation-redis": "^0.69.0", + "@opentelemetry/instrumentation-restify": "^0.66.0", + "@opentelemetry/instrumentation-router": "^0.65.0", + "@opentelemetry/instrumentation-runtime-node": "^0.34.0", + "@opentelemetry/instrumentation-socket.io": "^0.68.0", + "@opentelemetry/instrumentation-tedious": "^0.40.0", + "@opentelemetry/instrumentation-undici": "^0.31.0", + "@opentelemetry/instrumentation-winston": "^0.65.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.36.0", + "@opentelemetry/resource-detector-aws": "^2.21.0", + "@opentelemetry/resource-detector-azure": "^0.29.0", + "@opentelemetry/resource-detector-container": "^0.8.12", + "@opentelemetry/resource-detector-gcp": "^0.56.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.221.0" + } + }, + "@opentelemetry/configuration": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.221.0.tgz", + "integrity": "sha512-uE9y56Zdi9Gt/RdxYnVOo3YmFZkKJJMA0gqtBe8wh8gdtF5Asqe+Oh/TWiDtFb1s+31jNY4CWgnfIB1KOITfFA==", + "requires": { + "@opentelemetry/core": "2.10.0", + "yaml": "^2.8.3" + } + }, + "@opentelemetry/context-async-hooks": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz", + "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==", + "requires": {} + }, + "@opentelemetry/core": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", + "requires": { + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-txG1G0IrYSsKKMeiWZfj/i5cQmWB+h+hf3HzPpF3RqZVwp+iQQEIsv8Vtmzy6RWVdHdJZfygmVrBI39YTBvWcw==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + } + }, + "@opentelemetry/exporter-logs-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.221.0.tgz", + "integrity": "sha512-nKXkr4Tomi6fjYVOf+ytcW3dZAVr4v4Bv5gsT6dr2gvpUPJpKgHB4XbMufMsPotRE3g0XH2GwVVCkN2w6SON+Q==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + } + }, + "@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.221.0.tgz", + "integrity": "sha512-AH6EY+47gXFaWYgG3hfeOneGiE9xIZGtDBk+9g0sM8NZWzsQhhmqPbQQXJzS7pyCh5jRRr2nYNXVrkCmoojRvQ==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" + } + }, + "@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-KOgCtO15FC6C1T/xOqBcr7EyUs7B+7yomGNb5Y97d3s38rPbCCk5sewkmE2b0/itOkQ/PptX8CLlD+kn2mEtTg==", + "requires": { + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + } + }, + "@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.221.0.tgz", + "integrity": "sha512-sRfCKbOzgy8xZQV2as0RzIZlnCmCseCKZGLfRcrpo2CBngJDr+rPtX0zkG0+oUCV5kfQPUoW3W3C96Ag3Y/Clg==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0" + } + }, + "@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.221.0.tgz", + "integrity": "sha512-YMF4LveY2I3yhw61rn6nmC9FE8U24IZHPeKU1Duc5+sbwjMd8FwZAwba318ImdThCg/HuVQvhm2y6bfgNPnfYg==", + "requires": { + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + } + }, + "@opentelemetry/exporter-prometheus": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.221.0.tgz", + "integrity": "sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-zXminlZedtq9LvOW64CnNkOqk15zV75k8JgtdTuWFge6+jk2m4GmAUm6L2eIiG1o2a2bZxXw2PDrszm+bps0IA==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + } + }, + "@opentelemetry/exporter-trace-otlp-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.221.0.tgz", + "integrity": "sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + } + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.221.0.tgz", + "integrity": "sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==", + "requires": { + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" + } + }, + "@opentelemetry/exporter-zipkin": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.10.0.tgz", + "integrity": "sha512-7gsvgf0UDoJ4l9ObrwBmz5G/ZogiPk+lq+g5GpLp24YQF/vPM/BSsnOfcLnfinast5ASUgLo78uSC/ObjlnXgg==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/instrumentation": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.221.0.tgz", + "integrity": "sha512-cCk80Z/iRDf/5gfsKMB4f74LqVA5yKETB/9ojPzVW/6/f70iu89nJvGxsFCxx4XfSohaOofkU19kiYm84AiAlw==", + "requires": { + "@opentelemetry/api-logs": "0.221.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + } + }, + "@opentelemetry/instrumentation-amqplib": { + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.68.0.tgz", + "integrity": "sha512-U9Fc3C061q+AGxP3xEJTIAJtBduY1GL21J4SjOSxCmmls4UUva16jzQ5ZkunQe0pKalrRjZ/DlZ+wgfgQxqjBw==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + } + }, + "@opentelemetry/instrumentation-aws-lambda": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.73.0.tgz", + "integrity": "sha512-N2BZFlWmVt2zjpiqPnfmIlj7tV/wfKSZCFF/laLAnJSTZjSEdc9JYSXW+KUV0FMUNDfpLCeAcI1xDMdGLdxFJg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/propagator-aws-xray": "^2.1.4", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "^8.10.155" + } + }, + "@opentelemetry/instrumentation-aws-sdk": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.76.0.tgz", + "integrity": "sha512-gg2QaDtWeFezRt2mAl9vBQ38y60tzUShKg1KA9uTgsMJimUHaBnB3X8kEH9Of8jKWT4DDoDcSA37gPcoEc0hiA==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + } + }, + "@opentelemetry/instrumentation-bunyan": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.66.0.tgz", + "integrity": "sha512-IqYQC1dav35NHlD5nYnpBXK8tI6KJ9/MIt8LYKFxwlhMIwfBCfavaklyP8NtVKoJ0WZKzX2v9Sh+xGK1XvSniQ==", + "requires": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1", + "@types/bunyan": "1.8.11" + } + }, + "@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.66.0.tgz", + "integrity": "sha512-4ksN7PfXLg7raDyXIjIrtxxzuuDlUx6Fh0s8VXojdl+nn2o0xkYa9jrq1f9Bfqhm+Sce3GOa1RjE3ycvAwk6Xw==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + } + }, + "@opentelemetry/instrumentation-connect": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.64.0.tgz", + "integrity": "sha512-D1Tpom3BpY8g29FFOEQ2FZioVFjyXwXHsh3BOn2BHcg7Taipg+yc+DPGUwvdR4WZrKnNMAG/+FBXNa0S0KhJYA==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + } + }, + "@opentelemetry/instrumentation-cucumber": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.37.0.tgz", + "integrity": "sha512-ezhn6D0DSUZkwLBGdfnr+PAENvB92AhbEH/dkcSLYB8dbQiw/NQ8y19jOn3E6MZTt0+FD1YyHtrJS2skDO4nDQ==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-dataloader": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.38.0.tgz", + "integrity": "sha512-OmOVadK0m7sdlvMwbt1gb2iVUCyvVNDo3x5JLGgnKggLTJBgTcQxgMl3pAhFAMzWGo9URuMxuh3Bphy9Pb9nZw==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-dns": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.64.0.tgz", + "integrity": "sha512-La6s9SdKgojZQVFD7AclQBYe3WioVe6zicJswM3QPPPHpCufJYnw8rO/G9o2Yl/OUeS7PYpzwHh4N6lexzbEcA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-express": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.69.0.tgz", + "integrity": "sha512-91pHMujQgDyhEQrdg8RriMBrRZ/qPaJ0Y2dopQ6lHjW5YjoeytWi8ruM//T6f5o0D95hnqRlv79Pel1lGPqaYg==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-fs": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.40.0.tgz", + "integrity": "sha512-p39axaaYVKhnl5l4M+1aiXmxrAG2HuTti7DHxs2jDJRst828y5iwqUZLC1UWIKIhW9FfdV5gogXg+nRRhSc0EA==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-generic-pool": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.64.0.tgz", + "integrity": "sha512-wM939j8Ox5BBHoA0r/p9etdpyS3GcUf/sfrUx1dtZdqGKU4ZcBxLyPD8QntwFkSI9PHql+rRejTCA6Btktz8Kg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-graphql": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.69.0.tgz", + "integrity": "sha512-vyKzuiBoEulV1FjMSe4iiuwZedt+nNAuaSVOh/3WxjDIuGQ9WsH+0ohd9snhIY6guAjYOGjqvTiYLs6K+OTQQg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-grpc": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.221.0.tgz", + "integrity": "sha512-1U45172SiPWG1MPfrgLItIuXZO/RfJqt5sxsrdlKN1NRV0pUtv7lbpgB1nShwP/SGSKaAFkovbVg3a1hfy3mpQ==", + "requires": { + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/instrumentation-hapi": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.67.0.tgz", + "integrity": "sha512-cIXIN4vZXm6aI4yz+4oUIRnkiAxCIpONrMhnGTI+ILKKEsIXP8Uselfr9663+TnisrgRLB7kKp+SoOuGJRHGtw==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-host-metrics": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.4.0.tgz", + "integrity": "sha512-jnFyX2sTn2B+9mjsL3qgAHzpxdaia4/FV2GSlf9QrpaiMi6O0M0lA9JZTsf4FTvuOwrIngCk+MeVXjsxgBXXyg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "systeminformation": "^5.31.6" + } + }, + "@opentelemetry/instrumentation-http": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.221.0.tgz", + "integrity": "sha512-oIP91CPIANuYr09tGFElPFKAh6JUar+awJf1kBRYlaeo9b0gDwZHEB2zBfFlvdNFHm0wAVutMZODVi5smKT30g==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + } + }, + "@opentelemetry/instrumentation-ioredis": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.69.0.tgz", + "integrity": "sha512-I9sZtxXWZ1tRXtRNTEVxpokGtXy6RL1SZhtPVh7zxH78t8ar71V5Dx4bnQiUjKTDzItpC73krD8c0/cEWA9oLg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.33.0" + } + }, + "@opentelemetry/instrumentation-kafkajs": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.30.0.tgz", + "integrity": "sha512-/p/D4etxJpJGB0VrS+kqF8WfVAMFWf5ybhY0mjzIEd/d/T68+nxTWJaI5MJXFiyOnBUFuMlun12VJt+QJDCSZA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + } + }, + "@opentelemetry/instrumentation-knex": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.65.0.tgz", + "integrity": "sha512-rJTT12VlDnL6wOWfxnBvkTUIzW2ju+7nqlToMy1tlqL0j6ohlVPxryMCS5h6UqE3CFpq7HtHqVObQHZgrA37KA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.1" + } + }, + "@opentelemetry/instrumentation-koa": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.69.0.tgz", + "integrity": "sha512-fxuA8jFOdqQzJV9Sitd0dk+zns7RQCFe19ia3LHex5oLiQPaaQovBv37jndX/zAZw6EBORATePHE8OQUwraPCQ==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + } + }, + "@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.65.0.tgz", + "integrity": "sha512-s2KisLZ82iDvCF2QbsV1k1wrz3DMSBP9OiMfmNn5oSyaNT7jcNphR4uxr7WjwH+ssucuvhKWqKzJ2vdx7KRMVA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-memcached": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.64.0.tgz", + "integrity": "sha512-ek34tp7Qjci4CLahXybJ3aaixU1d2j28X5JSXSXbp6/rIiFGjMCAXJWw3FeiYmA82D/gV/wzPhL7r7m/p4gUzw==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/memcached": "^2.2.6" + } + }, + "@opentelemetry/instrumentation-mongodb": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.74.0.tgz", + "integrity": "sha512-GRnHu69YLQUYgguuYkKi6wpizMY4r7gLC08rSq8cg41p6t7+1YIy5nXoGC61NA7KCZUE9jbcDnzd45i32IblZg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + } + }, + "@opentelemetry/instrumentation-mongoose": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.67.0.tgz", + "integrity": "sha512-iEBgNrychD36qI16X/V8WZb2JabjQPE+pyrkLU320ApZyhGVsYU9LH3Nqt4Mkg1nFsa9qWhRQrCfhoANCgT6EA==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + } + }, + "@opentelemetry/instrumentation-mysql": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.67.0.tgz", + "integrity": "sha512-G4aRrVKcd2Aodqi7WzRZ3LQJNKrM8BpsXEVMHrOb9s8Lg0jZXNvlLY0NWL1yVJrjnqI/unwEsp2aVHkVImPMeA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" + } + }, + "@opentelemetry/instrumentation-mysql2": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.67.0.tgz", + "integrity": "sha512-AmviR7l0xMxhC83scY3u+NkkT6blhD/xK9tPi9nYtjNG1gwPtMgZjYOa3f9lGOwpXs/EwN7wiyAgxiO4KTcENA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.42.0" + } + }, + "@opentelemetry/instrumentation-nestjs-core": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.67.0.tgz", + "integrity": "sha512-lXb7pjobd2i/9Gmihf9wrOM0MgnDYBxOJq7uWEhZOqULodNFLyPCRUnWxSIbPQdUzlU36QtHjuAeaEBUJnqXkA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + } + }, + "@opentelemetry/instrumentation-net": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.65.0.tgz", + "integrity": "sha512-W82H8UvaSrWynpI510CNJbq2Aq6L4/zuR/dAvoCZVYeRiChi0LHMI8i3rPe3Tmau2WBwE0jimaWgOs0GCfIHbQ==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + } + }, + "@opentelemetry/instrumentation-openai": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.19.0.tgz", + "integrity": "sha512-zHz7m/aUMDyAap7UMzaaxDkbmxEyUOfMBfh+7KICNwTBmIOypMnuUWwXWAfIJLgl+2dnZlNzxZRknULeS4YIhg==", + "requires": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + } + }, + "@opentelemetry/instrumentation-oracledb": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.46.0.tgz", + "integrity": "sha512-nqxQvbp7HvsVPyDdgiZADPQX6B6ZUtLfm+XPuJHtQ3anxIcqU6qhJlrDIEM2LrZMShqf7bs84RHfDt2rgsp7hg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@types/oracledb": "6.5.2" + } + }, + "@opentelemetry/instrumentation-pg": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.73.0.tgz", + "integrity": "sha512-yf3tBVwLHB9cZNNPSToNrthx36ouPe4FctFxy7ya6vSJ6gaiKjNfA/IgFFeuBpZflEQhy6aesPqzZo8ZjFkvNg==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.42.0", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" + } + }, + "@opentelemetry/instrumentation-pino": { + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.67.0.tgz", + "integrity": "sha512-Hb6phi2x1bq23OIiesj4imQvXs9Y5MMLtpgXH9hOuMm9LpBYz2cxPNgpgZ/XATuRclP1eRPoSl399o5XKwfoIA==", + "requires": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1" + } + }, + "@opentelemetry/instrumentation-redis": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.69.0.tgz", + "integrity": "sha512-lyCIEW89cYhMwaUSMBzsKHdwH2wOoqmuwXOARJneo9UL55govLIUCbYYAJ457oM8kdKADymlY4+SUW0DKQeIHw==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-restify": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.66.0.tgz", + "integrity": "sha512-9zbnL0ML2jFgJmmG1XPQTqwopCogC8eAtUQ0SXvYb+Ux2yuOBOvSg9XvRk/hcQf6WsRMll47cELCMC+IaI9I+g==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-router": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.65.0.tgz", + "integrity": "sha512-ti9tDLFLhLoev5U/cGeQpFTlOjFFwrFbieZmwTFql9z8EijXDzFE7a3+3mgnxGl9CZR0luKCwFD/o51NPU+Ngg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/instrumentation-runtime-node": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.34.0.tgz", + "integrity": "sha512-Yb3PcmuK/iIOWY49GSEcSGl7fR4r6UqhZnuy5EWvFaqKqqs9SYnQeyQUEAbnBnSougRpwFBDbdN1SSGcvMhbtQ==", + "requires": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-socket.io": { + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.68.0.tgz", + "integrity": "sha512-Bhd0KApVBYV4WQMZZbKRYfvev7SudvCtSn6b36uyUbKowOMEoGpnVoIvm0rlrrBR0KmkcV3Y37SngCGUrTm3lg==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/instrumentation-tedious": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.40.0.tgz", + "integrity": "sha512-zTNNxs+KUJf1J+lHzeTDxAIZdVJYvQ8mvGUfyiWcVFgVdl7+4XV+wOBMSd1tZcRRlopfcVODDCOVMx/N7+zvcA==", + "requires": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/tedious": "^4.0.14" + } + }, + "@opentelemetry/instrumentation-undici": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.31.0.tgz", + "integrity": "sha512-qunCfgSFV+bjRdAYkWIjVX38jIN/Xj80CiERXXdzYAmdigCFvJPB3AY3j43bjJlkhgbJJSCGdbvQUSut8QIiyQ==", + "requires": { + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + } + }, + "@opentelemetry/instrumentation-winston": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.65.0.tgz", + "integrity": "sha512-hWSPnS530deRa+ttzY+QiGmgsK7aQHpqxbQRm56yO1j4qnIXuYYHaoSJ8/4lLOEcXB+hZs0mAAJ6QiMl1aaiGA==", + "requires": { + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0" + } + }, + "@opentelemetry/otlp-exporter-base": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.221.0.tgz", + "integrity": "sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-transformer": "0.221.0" + } + }, + "@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.221.0.tgz", + "integrity": "sha512-rQDmNgyiGCTrescjnzH2ntVyUKVIq6I2UjuK8+stT/Xg0ZOT71FVJqwjFdspQl6Yol/Yqsut9bDo+ame8oTmDQ==", + "requires": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.221.0.tgz", + "integrity": "sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==", + "requires": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0" + } + }, + "@opentelemetry/propagator-aws-xray": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", + "integrity": "sha512-Yjvt2EjL+tfpkVOdKbhTPgpM4SIAez9nG6Q/QjQ3yfcJcjIWp59ph70SLfvmkSL6++3DCnuBG3iWcB18PwWavQ==", + "requires": {} + }, + "@opentelemetry/propagator-b3": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.10.0.tgz", + "integrity": "sha512-GnA5B24H+1w8BO21J0q+IWNB0z1v+AGbcquTdIt/dufibhnhgxaA8YKvz0I3akRZhB1jHT+/tlzK+qlAjEDybQ==", + "requires": { + "@opentelemetry/core": "2.10.0" + } + }, + "@opentelemetry/propagator-jaeger": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.10.0.tgz", + "integrity": "sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==", + "requires": { + "@opentelemetry/core": "2.10.0" + } + }, + "@opentelemetry/redis-common": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", + "integrity": "sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==" + }, + "@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.36.0.tgz", + "integrity": "sha512-s75zJV1ShpYL5nk2cODfZY05Haw2hGxcfEFMu3ymvh2QU3HrhXaCW+rmNkhXhRrO8YophMFTyVdb7iCDleC/JQ==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + } + }, + "@opentelemetry/resource-detector-aws": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.21.0.tgz", + "integrity": "sha512-Veavy+khoywR+Hv065SU5jucFTGTiW1KXo39CsJ+8wqdYYz8jiRJPnQ20Kd+X9HbV2+Abb0l5CrJIdxK1ZOqBg==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + } + }, + "@opentelemetry/resource-detector-azure": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.29.0.tgz", + "integrity": "sha512-lWm0vjjlQMoc4Xvvd+dW/OZWT/SI4w+cIN7kbm8KimIZCr1EpAyvyQ7WEOrGoBoXCpQCrsZ18uKTooDgiBCGIw==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + } + }, + "@opentelemetry/resource-detector-container": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.12.tgz", + "integrity": "sha512-EJRFfIY26whY0w5RDxMRXlfBDgDS001JYMHuOVuDBBsRrV4MBqoVajR9B0L9Vy728+w/HNVnSQkpJFacFr+klg==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + } + }, + "@opentelemetry/resource-detector-gcp": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.56.0.tgz", + "integrity": "sha512-H8yNeqTsuapbXs6MLZTtelfUCk+5D8jD3+KosCJaXOyx5gl3EWWvs70HbNXTUO4VYLxccySFYJYVCd8YMM0NJw==", + "requires": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "gcp-metadata": "^8.0.0" + } + }, + "@opentelemetry/resources": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/sdk-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.221.0.tgz", + "integrity": "sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==", + "requires": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/sdk-metrics": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.10.0.tgz", + "integrity": "sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0" + } + }, + "@opentelemetry/sdk-node": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.221.0.tgz", + "integrity": "sha512-UbYuvtBrQQB5Prsh9KOKy4kxzexFxfMs5MkteHeWMoswsEB7kiNhyUVkAOFW/qsEzNHtrkgyghrD2ilZJa+5YA==", + "requires": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/configuration": "0.221.0", + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-logs-otlp-http": "0.221.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.221.0", + "@opentelemetry/exporter-prometheus": "0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-trace-otlp-http": "0.221.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.221.0", + "@opentelemetry/exporter-zipkin": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/propagator-b3": "2.10.0", + "@opentelemetry/propagator-jaeger": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0", + "@opentelemetry/sdk-trace-node": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", + "requires": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + } + }, + "@opentelemetry/sdk-trace-node": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz", + "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==", + "requires": { + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==" + }, + "@opentelemetry/sql-common": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.42.0.tgz", + "integrity": "sha512-nwUwUU+8O8a4bnLqk6CodWeegGMEANgC94KTAhXcpGWLrW/2/hek/0ajNbjXnSOoNuCX+nteUPs46HFHhou9Xw==", + "requires": { + "@opentelemetry/core": "^2.0.0" + } + }, + "@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": { + "@noble/hashes": "^1.1.5" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true + }, + "@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true + }, + "@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "requires": {} + }, + "@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "devOptional": true + }, + "@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "requires": { + "@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": { + "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==", + "devOptional": true + }, + "@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==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "@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==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.22.0" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==" + }, + "@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "requires": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==" + }, + "@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + } + }, + "@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true + }, + "@redocly/openapi-core": { + "version": "1.34.18", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.18.tgz", + "integrity": "sha512-UyKIm0wTPw5BcY7Z2PkbK1Ma260um96LSBWXHrdSMe+ZV0EPMyDfAcUcjjm3qEiGST9OK/1TriekdPCZkn4Q3A==", + "dev": true, + "requires": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.3.0", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "dependencies": { + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, + "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" + } + }, + "minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==" + }, + "@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==" + }, + "@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "requires": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + } + }, + "@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "requires": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + } + }, + "@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==" + }, + "@sentry/core": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.69.0.tgz", + "integrity": "sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg==", + "requires": { + "@sentry/conventions": "^0.16.0" + } + }, + "@sentry/node": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.69.0.tgz", + "integrity": "sha512-xEXA1YGIiTZbrW6MWV34uS6JGQuQg2ijTI0zed+FsJb9JZKPYel/GZK8Km26vfTVb+yCXFmWZNBesKegNcVdzg==", + "requires": { + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "@sentry/node-core": "10.69.0", + "@sentry/opentelemetry": "10.69.0", + "@sentry/server-utils": "10.69.0", + "import-in-the-middle": "^3.0.0" + }, + "dependencies": { + "@opentelemetry/api-logs": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", + "requires": { + "@opentelemetry/api": "^1.3.0" + } + }, + "@opentelemetry/instrumentation": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", + "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", + "requires": { + "@opentelemetry/api-logs": "0.220.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + } + } + } + }, + "@sentry/node-core": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.69.0.tgz", + "integrity": "sha512-IgArHczrZJxkgxoffHscj0NxQrG6kCazgmGQnlf3j58J1ec21YaUu8Tu+7G4Lo5tCiW3teQnwlKW1ttMXSqWRw==", + "requires": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "@sentry/opentelemetry": "10.69.0", + "import-in-the-middle": "^3.0.0" + } + }, + "@sentry/opentelemetry": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.69.0.tgz", + "integrity": "sha512-3FyWV6YcEJuvLrlaKGE1dHXCI+1YO0a62w7PkwlRg8yp6K6YXkmdwu9GjqaYD+Ju4tm7uC7mHIsGFQMm0M7pqQ==", + "requires": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0" + } + }, + "@sentry/server-utils": { + "version": "10.69.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.69.0.tgz", + "integrity": "sha512-0MwHrA8+nNvMIsqf8m3cXwCBlUjr6AS7N6CZvHJtY1DkqEvQqEbD5VIrhzEyHN/KMZIgQ8XeDCQRhjnXFQGRhg==", + "requires": { + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", + "@apm-js-collab/tracing-hooks": "^0.13.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.69.0", + "meriyah": "^6.1.4" + } + }, + "@simple-libs/child-process-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", + "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", + "dev": true, + "requires": { + "@simple-libs/stream-utils": "^2.0.0" + } + }, + "@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true + }, + "@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzc + "@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true + }, + "@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true + }, + "@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.1" + } + }, + "@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "requires": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "@stellar/js-xdr": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", + "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==" + }, + "@stellar/stellar-base": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.1.tgz", + "integrity": "sha512-gOBSOFDepihslcInlqnxKZdIW9dMUO1tpOm3AtJR33K2OvpXG6SaVHCzAmCFArcCqI9zXTEiSoh70T48TmiHJA==", + "requires": { + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "buffer": "^6.0.3", + "sha.js": "^2.3.6", + "sodium-native": "^4.1.1", + "tweetnacl": "^1.0.3" + } + }, + "@stellar/stellar-sdk": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-12.3.0.tgz", + "integrity": "sha512-F2DYFop/M5ffXF0lvV5Ezjk+VWNKg0QDX8gNhwehVU3y5LYA3WAY6VcCarMGPaG9Wdgoeh1IXXzOautpqpsltw==", + "requires": { + "@stellar/stellar-base": "^12.1.1", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "eventsource": "^2.0.2", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + } + }, + "@stryker-mutator/api": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-8.7.1.tgz", + "integrity": "sha512-56vxcVxIfW0jxJhr7HB9Zx6Xr5/M95RG9MUK1DtbQhlmQesjpfBBsrPLOPzBJaITPH/vOYykuJ69vgSAMccQyw==", + "dev": true, + "requires": { + "mutation-testing-metrics": "3.3.0", + "mutation-testing-report-schema": "3.3.0", + "tslib": "~2.7.0", + "typed-inject": "~4.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + } + } + }, + "@stryker-mutator/core": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-8.7.1.tgz", + "integrity": "sha512-r2AwhHWkHq6yEe5U8mAzPSWewULbv9YMabLHRzLjZnjj+Ipxtg+Zo22rrUc2Zl7mnYvb9w34bdlEzGz6MKgX2g==", + "dev": true, + "requires": { + "@inquirer/prompts": "^6.0.0", + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/instrumenter": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "ajv": "~8.17.1", + "chalk": "~5.3.0", + "commander": "~12.1.0", + "diff-match-patch": "1.0.5", + "emoji-regex": "~10.4.0", + "execa": "~9.4.0", + "file-url": "~4.0.0", + "lodash.groupby": "~4.6.0", + "minimatch": "~9.0.5", + "mutation-testing-elements": "3.4.0", + "mutation-testing-metrics": "3.3.0", + "mutation-testing-report-schema": "3.3.0", + "npm-run-path": "~6.0.0", + "progress": "~2.0.3", + "rxjs": "~7.8.1", + "semver": "^7.6.3", + "source-map": "~0.7.4", + "tree-kill": "~1.2.2", + "tslib": "2.7.0", + "typed-inject": "~4.0.0", + "typed-rest-client": "~2.1.0" + }, + "dependencies": { + "@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + } + }, + "@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "requires": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + } + }, + "@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + } + }, + "@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + } + }, + "@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + } + }, + "@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "dev": true, + "requires": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + } + }, + "@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "dev": true, + "requires": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "requires": { + "mute-stream": "^1.0.0" + } + }, + "@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" + } + }, + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "execa": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.4.1.tgz", + "integrity": "sha512-5eo/BRqZm3GYce+1jqX/tJ7duA2AnE39i88fuedNFUV8XxGxUpF3aWkBRfbUcjV49gCkvS/pzc0YrCPhaIewdg==", + "dev": true, + "requires": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + } + }, + "get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "requires": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + } + }, + "human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true + }, + "is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true + }, + "mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true + }, + "npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "requires": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true + }, + "tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + } + } + }, + "@stryker-mutator/instrumenter": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-8.7.1.tgz", + "integrity": "sha512-HSq4VHXesQCMR3hr6bn41DAeJ0yuP2vp9KSnls2TySNawFVWOCaKXpBX29Skj3zJQh7dnm7HuQg8HuXvJK15oA==", + "dev": true, + "requires": { + "@babel/core": "~7.25.2", + "@babel/generator": "~7.25.0", + "@babel/parser": "~7.25.0", + "@babel/plugin-proposal-decorators": "~7.24.7", + "@babel/plugin-proposal-explicit-resource-management": "^7.24.7", + "@babel/preset-typescript": "~7.24.7", + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "angular-html-parser": "~6.0.2", + "semver": "~7.6.3", + "weapon-regex": "~1.3.2" + }, + "dependencies": { + "@babel/core": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.9.tgz", + "integrity": "sha512-WYvQviPw+Qyib0v92AwNIrdLISTp7RfDkM7bPqBvpbnhY4wq8HvHBZREVdYDXk98C8BkOIVnHAY3yvj7AVISxQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helpers": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "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 + } + } + }, + "@babel/generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.9.tgz", + "integrity": "sha512-omlUGkr5EaoIJrhLf9CJ0TvjBRpd9+AXRG//0GEQ9THSo8wPiTlbpy1/Ow8ZTrbXpjd9FHXfbFQx32I04ht0FA==", + "dev": true, + "requires": { + "@babel/types": "^7.25.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + } + }, + "@babel/parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", + "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", + "dev": true, + "requires": { + "@babel/types": "^7.25.9" + } + }, + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + } + } + }, + "@stryker-mutator/jest-runner": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/jest-runner/-/jest-runner-8.7.1.tgz", + "integrity": "sha512-507jgu9E0yNDwMN56p4J+iFI77+rPDLDV91qYGbBI6fvy5c7rBQ63l64FtAFMTG75f4gCZ6C/Pq3nXTQx6PMjA==", + "dev": true, + "requires": { + "@stryker-mutator/api": "8.7.1", + "@stryker-mutator/util": "8.7.1", + "semver": "~7.6.3", + "tslib": "~2.7.0" + }, + "dependencies": { + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + }, + "tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + } + } + }, + "@stryker-mutator/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-8.7.1.tgz", + "integrity": "sha512-Oj/sIHZI1GLfGOHKnud4Gw0ZRufm7ONoQYNnhcaAYEXTWraYVcV7mue/th8fZComTHvDPA8Ge8U16FvWYEb8dg==", + "dev": true + }, + "@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "requires": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + } + }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@types/aws-lambda": { + "version": "8.10.162", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz", + "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==" + }, + "@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": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@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": { + "@babel/types": "^7.0.0" + } + }, + "@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": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@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": { + "@babel/types": "^7.28.2" + } + }, + "@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": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "requires": { + "@types/node": "*" + } + }, + "@types/conventional-commits-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", + "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==" + }, + "@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@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": { + "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": { + "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": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@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": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "requires": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "@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/luxon": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.5.tgz", + "integrity": "sha512-jJ41Q4z6ZVO260MNDdHfW7+7a5iMiX8Mr6ZJHcmgrvhZha6dz5704o/lF2kKl6URjH6ivEL97w9xS/MgpJEphg==" + }, + "@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "requires": { + "@types/node": "*" + } + }, + "@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/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz", + "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", + "requires": { + "undici-types": "~8.9.0" + }, + "dependencies": { + "undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==" + } + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node-int64": { + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", + "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/parquetjs": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/parquetjs/-/parquetjs-0.10.6.tgz", + "integrity": "sha512-ZCsD6j97YD0mGU8/VnVs3NjORXa7zeHvqlpJpCqy4jU8a1O21dalL+MFn9QNbdEfy8rszR1N7NHeT7/LdtHf+A==", + "dev": true, + "requires": { + "@types/node-int64": "*" + } + }, + "@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "@types/pg-pool": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", + "requires": { + "@types/pg": "*" + } + }, + "@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": { + "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": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "requires": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + }, + "dependencies": { + "@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + } + } + }, + "@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/superagent": { + "version": "8.1.11", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz", + "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==", + "dev": true, + "requires": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "@types/supertest": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.1.tgz", + "integrity": "sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==", + "dev": true, + "requires": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "requires": { + "@types/node": "*" + } + }, + "@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, + "@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/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, + "@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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": { + "@types/yargs-parser": "*" + } + }, + "@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": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", + "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/type-utils": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", + "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1" + } + }, + "@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "eslint-visitor-keys": "^5.0.0" + } + }, + "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 + }, + "ignore": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", + "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", + "dev": true + }, + "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, + "requires": {} + } + } + }, + "@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/project-service": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", + "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.70.1", + "@typescript-eslint/types": "^8.70.1", + "debug": "^4.4.3" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", + "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/type-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", + "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", + "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.70.1", + "@typescript-eslint/tsconfig-utils": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "eslint-visitor-keys": "^5.0.0" + } + }, + "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.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, + "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 + }, + "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" + } + }, + "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, + "requires": {} + } + } + }, + "@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", + "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", + "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1" + } + }, + "@typescript-eslint/types": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", + "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.70.1", + "@typescript-eslint/tsconfig-utils": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.70.1", + "eslint-visitor-keys": "^5.0.0" + } + }, + "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.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, + "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 + }, + "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" + } + }, + "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, + "requires": {} + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + } + }, + "@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true + }, + "@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + } + }, + "@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "dev": true, + "optional": true + }, + "@vortex/solver-sdk": { + "version": "file:packages/solver-sdk", + "requires": { + "viem": "^2.44.4", + "ws": "^8.18.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "requires": {} + }, + "accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "requires": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + } + }, + "acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true + }, + "acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "requires": {} + }, + "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, + "requires": {} + }, + "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": { + "debug": "4" + } + }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "angular-html-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-6.0.2.tgz", + "integrity": "sha512-8+sH1TwYxv8XsQes1psxTHMtWRBbJFA/jY0ThqpT4AgCiRdhTtRxru0vlBfyRJpL9CHd3G06k871bR2vyqaM6A==", + "dev": true, + "requires": { + "tslib": "^2.6.2" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "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": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "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 + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true + }, + "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": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "dependencies": { + "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 + } + } + }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "argue-cli": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", + "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==" + }, + "async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "axios": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "requires": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "requires": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "requires": { + "@types/babel__core": "^7.20.5" + } + }, + "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": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + } + }, + "babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "bare-addon-resolve": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.1.tgz", + "integrity": "sha512-F/SD2du8keuYSb4xipnGz5j2E6yhNdHA8ZVxtHae6h2uOrpBIjjbhXvjzKZbr5XUOzqBzh/i8GVFycj2DlFQIA==", + "optional": true, + "requires": { + "bare-module-resolve": "^1.10.0", + "bare-semver": "^1.0.0" + } + }, + "bare-module-resolve": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.4.tgz", + "integrity": "sha512-xcfgg2u7HqgJiBmah71O9vvdFAgHCvkqC/WSC2O7Bbgosoc1eC/BWe/6IDJ4OsfKlkxuvC/TDWXC+oH5yeW8mA==", + "optional": true, + "requires": { + "bare-semver": "^1.0.0" + } + }, + "bare-semver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz", + "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==", + "optional": true + }, + "base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "baseline-browser-mapping": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.5.tgz", + "integrity": "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A==", + "dev": true + }, + "bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==" + }, + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g==", + "optional": true + }, + "bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "requires": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "dependencies": { + "content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==" + } + } + }, + "brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "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": { + "fill-range": "^7.1.1" + } + }, + "brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "requires": { + "base64-js": "^1.1.2" + } + }, + "browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "requires": { + "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" + } + }, + "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": { + "fast-json-stable-stringify": "2.x" + } + }, + "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": { + "node-int64": "^0.4.0" + } + }, + "bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "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==" + }, + "bullmq": { + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-6.3.9.tgz", + "integrity": "sha512-wBGk4tHZRFcQayZlC5DPRxg2EFfCqhgkJq0uQ8EvufMgh2hYmPeH8tK+1U4ffimNmnJzTUhwwW1PNUIQMhvSpQ==", + "requires": { + "cron-parser": "5.10.1", + "msgpackr": "2.1.0", + "node-abort-controller": "3.1.1", + "semver": "7.8.5", + "tslib": "2.8.1" + } + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + } + }, + "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": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "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": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "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 + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true + }, + "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 + }, + "chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true + }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "requires": { + "readdirp": "^4.0.1" + } + }, + "chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true + }, + "ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true + }, + "cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==" + }, + "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": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.15.1.tgz", + "integrity": "sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==", + "requires": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true + }, + "cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, + "cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true + }, + "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==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "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==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "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==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "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": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "requires": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "dependencies": { + "color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "requires": { + "color-name": "^2.0.0" + } + }, + "color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==" + } + } + }, + "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": { + "color-name": "~1.1.4" + } + }, + "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-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "requires": { + "color-name": "^2.0.0" + }, + "dependencies": { + "color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==" + } + } + }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "comment-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", + "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "dev": true, + "requires": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + } + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "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 + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "conventional-changelog-angular": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", + "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", + "dev": true, + "requires": { + "@conventional-changelog/template": "^1.4.0" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "dev": true, + "requires": { + "@conventional-changelog/template": "^1.4.0" + } + }, + "conventional-commits-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "dev": true, + "requires": { + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" + } + }, + "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": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, + "cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==" + }, + "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": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "cron": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.4.0.tgz", + "integrity": "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==", + "requires": { + "@types/luxon": "~3.7.0", + "luxon": "~3.7.0" + } + }, + "cron-parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-5.10.1.tgz", + "integrity": "sha512-pKRrRagItwk9rGIStcUyMxFX34x56zoX3KDf9HJ4ttwkXIK1qxK63EvXvEDmlxI9AdPJ+Y7TEKRftRlW5eSS7A==", + "requires": { + "luxon": "^3.7.2" + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "requires": { + "ms": "^2.1.3" + } + }, + "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, + "requires": {} + }, + "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": { + "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 + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "optional": true + }, + "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 + }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==" + }, + "dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "requires": { + "dotenv": "^16.4.5" + } + }, + "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": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "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==" + }, + "ejs": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-6.0.1.tgz", + "integrity": "sha512-UaaM14yby8U3k02ihS1Bmj5Kz2d7CCQM1scxpgs4Mhkq8F1wR2gl3+Ts4h5Ne4Mnt7M9m4Dw7jsuMr3+xO4vZA==" + }, + "electron-to-chromium": { + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "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==" + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "enhanced-resolve": { + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "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": { + "is-arrayish": "^0.2.1" + } + }, + "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==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==" + }, + "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": { + "es-errors": "^1.3.0" + } + }, + "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==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "es-toolkit": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", + "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", + "dev": true + }, + "esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, + "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": { + "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": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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" + } + }, + "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 + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "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 + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "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 + }, + "esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "requires": { + "estraverse": "^5.1.0" + } + }, + "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": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true + }, + "expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "requires": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + } + }, + "express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "requires": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "fast-check": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.10.2.tgz", + "integrity": "sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==", + "dev": true, + "requires": { + "pure-rand": "^8.0.0" + }, + "dependencies": { + "pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "dev": true + } + } + }, + "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-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "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" + } + } + } + }, + "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": { + "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": { + "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==" + }, + "fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true + }, + "fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "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": { + "bser": "2.1.1" + } + }, + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "requires": { + "is-unicode-supported": "^2.0.0" + }, + "dependencies": { + "is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "requires": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + } + }, + "file-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-4.0.0.tgz", + "integrity": "sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==", + "dev": true + }, + "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": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "requires": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + } + }, + "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": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "dev": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==" + }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "requires": { + "is-callable": "^1.2.7" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, + "fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "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==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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": { + "mime-db": "1.52.0" + } + } + } + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "requires": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==" + }, + "fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "requires": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "requires": { + "glob": "^10.3.7" + } + } + } + }, + "gcp-metadata": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", + "requires": { + "gaxios": "7.1.3", + "google-logging-utils": "1.1.3", + "json-bigint": "^1.0.0" + } + }, + "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 + }, + "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==" + }, + "get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true + }, + "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": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "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 + }, + "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": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "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 + }, + "glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "requires": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "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.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "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" + } + } + } + }, + "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": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "requires": { + "ini": "6.0.0" + } + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==" + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "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 + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "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 + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } + }, + "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": { + "function-bind": "^1.1.2" + } + }, + "helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==" + }, + "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": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, + "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": { + "agent-base": "6", + "debug": "4" + } + }, + "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 + }, + "husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true + }, + "iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true + }, + "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": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-in-the-middle": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.2.tgz", + "integrity": "sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==", + "requires": { + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + } + }, + "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": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true + }, + "int53": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", + "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==" + }, + "ioredis": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", + "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", + "requires": { + "@ioredis/commands": "2.0.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "standard-as-callback": "2.1.0" + } + }, + "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==" + }, + "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-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "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==" + }, + "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 + }, + "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": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "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 + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "requires": { + "which-typed-array": "^1.1.16" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "requires": {} + }, + "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 + }, + "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": { + "@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" + } + }, + "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": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + } + }, + "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": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "requires": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + } + }, + "jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "requires": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "requires": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + } + }, + "jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "requires": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + } + }, + "jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + } + } + }, + "jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "requires": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + } + }, + "jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "requires": { + "detect-newline": "^3.1.0" + } + }, + "jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + } + }, + "jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "requires": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + } + }, + "jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "fsevents": "^2.3.3", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + } + }, + "jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + } + }, + "jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + } + }, + "jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + } + }, + "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, + "requires": {} + }, + "jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true + }, + "jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + } + }, + "jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "requires": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + } + }, + "jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "requires": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "requires": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + } + } + }, + "jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + } + }, + "jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "requires": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + } + }, + "jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "requires": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + } + }, + "jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "requires": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "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" + } + } + } + }, + "jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true + }, + "joi": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", + "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", + "requires": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.1.0" + } + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true + }, + "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": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz", + "integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "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": { + "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": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "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": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "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": { + "json-buffer": "3.0.1" + } + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "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 + }, + "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": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "libphonenumber-js": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.9.tgz", + "integrity": "sha512-VNS5vWMM7r0P66BYv+TQJATxExEgLxN+34hfHDVhDkUsGAE4cRg0shCNSLTXNKm7nIUscC7AfB51TjxEeF7msQ==" + }, + "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 + }, + "load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==" + }, + "loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "dev": true + }, + "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": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true + }, + "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": { + "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 + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "requires": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + } + } + }, + "long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "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" + } + }, + "luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==" + }, + "lzo": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/lzo/-/lzo-0.4.11.tgz", + "integrity": "sha512-apQHNoW2Alg72FMqaC/7pn03I7umdgSVFt2KRkCXXils4Z9u3QBh1uOtl2O5WmZIDLd9g6Lu4lIdOLmiSTFVCQ==", + "optional": true, + "requires": { + "bindings": "~1.2.1" + } + }, + "magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "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" + } + }, + "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": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==" + }, + "memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.4" + } + }, + "merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==" + }, + "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 + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "meriyah": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz", + "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "dependencies": { + "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 + } + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==" + }, + "mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "requires": { + "mime-db": "^1.54.0" + } + }, + "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 + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==" + }, + "module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "msgpackr": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.0.tgz", + "integrity": "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==", + "requires": { + "msgpackr-extract": "^3.0.4" + } + }, + "msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "optional": true, + "requires": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4", + "node-gyp-build-optional-packages": "5.2.2" + } + }, + "multer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "dependencies": { + "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==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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": { + "mime-db": "1.52.0" + } + }, + "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": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + } + } + }, + "mutation-testing-elements": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-3.4.0.tgz", + "integrity": "sha512-zFJtGlobq+Fyq95JoJj0iqrmwLSLQyIJuDATLwFMDSJCxpGN8kHCA6S4LoLJnkSL6bg4Aqultp8OBSMxGbW3EA==", + "dev": true + }, + "mutation-testing-metrics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-3.3.0.tgz", + "integrity": "sha512-vZEJ84SpK3Rwyk7k28SORS5o6ZDtehwifLPH6fQULrozJqlz2Nj8vi52+CjA+aMZCyyKB+9eYUh1HtiWVo4o/A==", + "dev": true, + "requires": { + "mutation-testing-report-schema": "3.3.0" + } + }, + "mutation-testing-report-schema": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-3.3.0.tgz", + "integrity": "sha512-DF56q0sb0GYzxYUYNdzlfQzyE5oJBEasz8zL76bt3OFJU8q4iHSdUDdihPWWJD+4JLxSs3neM/R968zYdy0SWQ==", + "dev": true + }, + "mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true + }, + "napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true + }, + "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": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" + }, + "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-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "optional": true, + "requires": { + "detect-libc": "^2.0.1" + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "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 + }, + "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 + }, + "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": { + "path-key": "^3.0.0" + } + }, + "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==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + }, + "object-stream": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/object-stream/-/object-stream-0.0.1.tgz", + "integrity": "sha512-+NPJnRvX9RDMRY9mOWOo/NDppBjbZhXirNNSu2IBnuNboClC9h1ZGHXgHBLDbJMHsxeJDq922aVmG5xs24a/cA==" + }, + "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": { + "ee-first": "1.1.1" + } + }, + "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": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "requires": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + } + }, + "supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true + }, + "type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true + } + } + }, + "optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "requires": { + "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" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "ox": { + "version": "0.14.45", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.45.tgz", + "integrity": "sha512-jpsQ+p0JZh9mKCxxzxz0d5qFHZZg2/O9xW18IpEC/dNXXPVqmDJ0c//9RMz0CILkbSGPA42+cbU0fC/ghwJ03w==", + "requires": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + } + }, + "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": { + "yocto-queue": "^0.1.0" + } + }, + "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": { + "p-limit": "^3.0.2" + } + }, + "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 + }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "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": { + "callsites": "^3.0.0" + } + }, + "parquetjs": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/parquetjs/-/parquetjs-0.11.2.tgz", + "integrity": "sha512-Y6FOc3Oi2AxY4TzJPz7fhICCR8tQNL3p+2xGQoUAMbmlJBR7+JJmMrwuyMjIpDiM7G8Wj/8oqOH4UDUmu4I5ZA==", + "requires": { + "brotli": "^1.3.0", + "bson": "^1.0.4", + "int53": "^0.2.4", + "lzo": "^0.4.0", + "object-stream": "0.0.1", + "snappyjs": "^0.6.0", + "thrift": "^0.11.0", + "varint": "^5.0.0" + } + }, + "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": { + "@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" + } + }, + "parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "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 + }, + "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==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "requires": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "dependencies": { + "lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true + } + } + }, + "path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pg": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", + "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", + "requires": { + "pg-cloudflare": "^1.4.0", + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + } + }, + "pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "optional": true + }, + "pg-connection-string": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg== + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "requires": {} + }, + "pg-protocol": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.6.tgz", + "integrity": "sha512-lqIfH7bdgsxHAY/ZnUOwm+aCFKrsHBDhSFuk9O0B9uCqJAIkrKTo/+LQqLPLUS4e04+jCmQVikxE3QipH5chPw==", + "requires": { + "split2": "^4.1.0" + } + }, + "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": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true + }, + "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 + }, + "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": { + "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" + } + } + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==" + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "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 + }, + "pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "requires": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "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 + } + } + }, + "pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "requires": { + "parse-ms": "^4.0.0" + } + }, + "prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "devOptional": true, + "requires": { + "@prisma/engines": "5.22.0", + "fsevents": "2.3.3" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prom-client": { + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", + "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", + "requires": { + "@opentelemetry/api": "^1.4.0", + "tdigest": "^0.1.1" + } + }, + "protobufjs": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + } + }, + "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": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==" + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "requires": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==" + }, + "raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "requires": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + } + }, + "react-is-18": { + "version": "npm:react-is@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 + }, + "react-is-19": { + "version": "npm:react-is@19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true + }, + "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": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true + }, + "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==" + }, + "redis-info": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redis-info/-/redis-info-3.1.0.tgz", + "integrity": "sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "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-addon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", + "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", + "optional": true, + "requires": { + "bare-addon-resolve": "^1.3.0" + } + }, + "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==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "requires": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + } + }, + "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": { + "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 + } + } + }, + "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 + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "requires": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "requires": { + "tslib": "^2.1.0" + } + }, + "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==" + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "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 + } + } + }, + "semifies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", + "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==" + }, + "semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==" + }, + "send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "requires": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + } + }, + "serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "requires": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + } + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "requires": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + } + }, + "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==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "requires": { + "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" + } + }, + "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": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + } + }, + "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": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "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": { + "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" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snappyjs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.6.1.tgz", + "integrity": "sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==" + }, + "sodium-native": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", + "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", + "optional": true, + "requires": { + "require-addon": "^1.1.0" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "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": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "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 + } + } + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "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-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, + "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": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "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==" + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, + "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" + } + }, + "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": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "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 + }, + "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 + }, + "strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "requires": { + "@tokenizer/token": "^0.3.0" + } + }, + "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": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + } + }, + "supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "requires": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "swagger-ui-dist": { + "version": "5.32.8", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.8.tgz", + "integrity": "sha512-dgMdWXIgnI4zX4OPhKEdWnlDODbgm8W3AX0Ivn/BBqcUh6xZsBxhZMnvk6DJyRz1BTrj8dPxtarmEGgkz30oyA==", + "requires": { + "@scarf/scarf": "=1.4.0" + } + }, + "symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true + }, + "synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "requires": { + "@pkgr/core": "^0.3.6" + } + }, + "systeminformation": { + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.1.tgz", + "integrity": "sha512-DEN6ICHk3Tk0Uf/hrAHh7xlt7iL5CJFBtPZinA0H62DrGG/KPKqq/Nzj6lCXPS4Ay/sf/14zNnk9LpqKzBIc+w==" + }, + "tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true + }, + "tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "requires": { + "bintrees": "1.0.2" + } + }, + "terser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "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 + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "dependencies": { + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "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" + } + } + } + }, + "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": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "thrift": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/thrift/-/thrift-0.11.0.tgz", + "integrity": "sha512-UpsBhOC45a45TpeHOXE4wwYwL8uD2apbHTbtBvkwtUU4dNwCjC7DpQTjw2Q6eIdfNtw+dKthdwq94uLXTJPfFw==", + "requires": { + "node-int64": "^0.4.0", + "q": "^1.5.0", + "ws": ">= 2.2.3" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + + }, + "tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true + }, + "tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "requires": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "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-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "requires": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + } + }, + "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": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "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": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, + "toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "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 + }, + "triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" + }, + "ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "requires": {} + }, + "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": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "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 + } + } + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.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 + } + } + }, + "tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + } + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "tsx": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "dev": true, + "requires": { + "esbuild": "~0.28.0", + "fsevents": "~2.3.3" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "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": { + "prelude-ls": "^1.2.1" + } + }, + "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 + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "requires": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "dependencies": { + "content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==" + } + } + }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-inject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-4.0.0.tgz", + "integrity": "sha512-OuBL3G8CJlS/kjbGV/cN8Ni32+ktyyi6ADDZpKvksbX0fYBV5WcukhRCYa7WqLce7dY/Br2dwtmJ9diiadLFpg==", + "dev": true + }, + "typed-rest-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", + "integrity": "sha512-Nel9aPbgSzRxfs1+4GoSB4wexCF+4Axlk7OSGVQCMa+4fWcyxIsN/YNmkp0xTT2iQzMD98h8yFLav/cNaULmRA==", + "dev": true, + "requires": { + "des.js": "^1.1.0", + "js-md4": "^0.3.2", + "qs": "^6.10.3", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true + }, + "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 + }, + "uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "requires": { + "@lukeed/csprng": "^1.0.0" + } + }, + "uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==" + }, + "underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true + }, + "undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==" + }, + "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 + }, + "unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "requires": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2", + "napi-postinstall": "^0.3.4" + } + }, + "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": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + } + }, + "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": { + "punycode": "^2.1.0" + } + }, + "uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true + }, + "urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==" + }, + "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": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, + "valibot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.5.0.tgz", + "integrity": "sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ==", + "requires": {} + }, + "validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==" + }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "viem": { + "version": "2.56.9", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.56.9.tgz", + "integrity": "sha512-UC1G0Qz+RbPKqwl6yRrp3dH6ltrOmUcFJMTcOyE1Mg0nO5lxrEBTONh/8OFWPnALGuN/O7LkNcz/IsSj83Svqw==", + "requires": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.14.45", + "ws": "8.21.0" + }, + "dependencies": { + "ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "requires": {} + } + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "weapon-regex": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-1.3.6.tgz", + "integrity": "sha512-wsf1m1jmMrso5nhwVFJJHSubEBf3+pereGd7+nBKtYJ18KoB/PWJOHS3WRkwS04VrOU0iJr2bZU+l1QaTJ+9nA==", + "dev": true + }, + "web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" + }, + "webpack": { + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.1", + "mime-db": "^1.54.0", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "dependencies": { + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true + }, + "webpack-sources": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, + "winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "requires": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "dependencies": { + "@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + } + } + }, + "winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "requires": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, + "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": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "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": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + } + }, + "ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "requires": {} + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==" + }, + "yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true + }, + "yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "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==" + }, + "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 + }, + "yoctocolors": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", + "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", + "dev": true + }, + "yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true + }, + "zod": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==" + } + } +} diff --git a/package.json b/package.json index e69de29b..9ea1a970 100644 --- a/package.json +++ b/package.json @@ -0,0 +1,116 @@ +{ + "name": "vortex-backend", + "version": "0.1.0", + "private": true, + "workspaces": [ + "packages/*" + ], + "license": "MIT", + "scripts": { + "dev": "nest start --watch", + "build": "nest build", + "start": "node dist/main.js", + "lint": "eslint src test scripts tools packages/solver-sdk/src packages/solver-sdk/examples --ext .ts", + "typecheck": "tsc --noEmit", + "test": "jest", + "test:e2e": "jest --config ./test/jest-e2e.json", + "test:scripts": "jest --config scripts/jest.config.js", + "check:migrations": "tsx scripts/check-migrations.ts", + "test:ci": "node scripts/ci/run-tests.mjs --suite=unit", + "test:e2e:ci": "node scripts/ci/run-tests.mjs --suite=e2e", + "coverage:merge": "node scripts/ci/coverage-merge.mjs", + "check:quarantine": "node scripts/ci/check-quarantine.mjs", + "test:mutation": "stryker run", + "check:env-drift": "tsx scripts/check-env-drift.ts", + "seed": "tsx scripts/seed.ts", + "solver:demo": "tsx scripts/solver-bot.ts", + "canary": "tsx tools/canary/canary.ts", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate dev", + "db:migrate:prod": "prisma migrate deploy", + "db:studio": "prisma studio", + "db:validate": "prisma validate", + "generate:client": "npm run build && tsx scripts/generate-client.ts", + "export:datasets": "tsx scripts/export-datasets.ts", + "prepare": "husky" + }, + "dependencies": { + "@bull-board/api": "^9.10.1", + "@bull-board/express": "^9.10.1", + "@nestjs/common": "^11.1.28", + "@nestjs/config": "^4.0.4", + "@nestjs/core": "^11.1.28", + "@nestjs/event-emitter": "^12.0.1", + "@nestjs/platform-express": "^11.1.28", + "@nestjs/platform-ws": "^11.1.28", + "@nestjs/schedule": "^6.1.3", + "@nestjs/schedule": "^12.0.2", + "@nestjs/swagger": "^11.4.5", + "@nestjs/throttler": "^6.5.0", + "@nestjs/websockets": "^11.1.28", + "@openfeature/core": "^1.12.0", + "@openfeature/server-sdk": "^1.23.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0", + "@opentelemetry/sdk-node": "^0.221.0", + "@opentelemetry/sdk-trace-node": "^2.10.0", + "@prisma/client": "5.22.0", + "@sentry/node": "^10.69.0", + "@stellar/stellar-sdk": "^12.0.0", + "bullmq": "^6.3.9", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "helmet": "^7.1.0", + "ioredis": "^6.0.0", + "joi": "^18.2.9", + "parquetjs": "^0.11.2", + "pg": "8.23.0", + "prom-client": "^15.1.3", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2", + "undici": "^8.11.2", + "uuid": "^10.0.0", + "viem": "^2.44.4", + "winston": "^3.13.0", + "ws": "^8.18.0", + "zod": "^4.6.5", + "@msgpack/msgpack": "^3.0.0" + }, + "devDependencies": { + "@commitlint/cli": "^21.2.3", + "@commitlint/config-conventional": "^21.2.3", + "@nestjs/cli": "^11.0.24", + "@nestjs/schematics": "^11.1.0", + "@nestjs/testing": "^12.1.0", + "@stryker-mutator/core": "^8.0.0", + "@stryker-mutator/jest-runner": "^8.0.0", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.25", + "@types/jest": "^30.0.0", + "@types/node": "^26.6.3", + "@types/parquetjs": "^0.10.6", + "@types/supertest": "^7.2.0", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.5.12", + "@msgpack/msgpack": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^8.70.1", + "@typescript-eslint/parser": "^7.13.0", + "eslint": "^8.57.0", + "fast-check": "^4.10.2", + "husky": "^9.1.7", + "jest": "^30.4.2", + "openapi-typescript": "^7.8.0", + "prisma": "5.22.0", + "supertest": "^7.2.2", + "ts-jest": "^29.4.11", + "tsx": "^4.15.4", + "typescript": "^5.4.5" + }, + "allowScripts": { + "prisma": true, + "@prisma/client": true, + "@prisma/engines": true + } +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e69de29b..36791386 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -0,0 +1,599 @@ +// ─── Prisma Schema ──────────────────────────────────────────────────────────── +// Database: PostgreSQL (swap to sqlite for local dev / CI without a real DB) +// Run `npm run db:generate` after editing this file. +// ───────────────────────────────────────────────────────────────────────────── + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +// ─── Enums ─────────────────────────────────────────────────────────────────── + +enum IntentState { + open + accepted + filled + cancelled + expired + slashed +} + +enum SupportedChain { + stellar + ethereum + base + polygon + arbitrum + optimism + avalanche +} + +/// Registry lifecycle. Delisted rows stay in the table so existing intents +/// that already copied the token metadata keep resolving. +enum TokenStatus { + active + paused + delisted + + @@map("token_status") +} + +// ─── Kill-switch scopes (issue #477) ────────────────────────────────────────── +// A switch is addressed by exactly one of four mutually-exclusive scopes. +// `global` has no chain/token; `chain` sets chain only; `token` sets chain + +// token; `operation` narrows further to a single operation name. +// Evaluation walks GLOBAL → CHAIN → TOKEN → OPERATION and the most specific +// matching switch wins, so an operation-level resume can never re-open a +// scope that a broader switch is still holding closed. + +enum KillSwitchScope { + global + chain + token + operation +} + +/// Operations a switch can gate. Constrained in the application layer (DTO + +/// normalisation) before it reaches the database, so the enum here is a +/// backstop rather than the only gate. +/// Changing this list requires a migration, since Prisma maps it to a real +/// Postgres enum type. +enum KillSwitchOperation { + create + accept + fill + slash + onchain +} + +// ─── Intent ────────────────────────────────────────────────────────────────── +// Represents a cross-chain swap request submitted by a user. +// The nested token objects (srcToken / dstToken) are stored as JSONB so the +// schema remains flexible while the intent's own scalar fields stay queryable. + +model Intent { + id String @id @default(uuid()) @map("id") + /// Externally-visible intent identifier (UUID). + intentId String @unique @map("intent_id") + /// Stellar / EVM address of the user that created the intent. + user String @map("user") + srcChain SupportedChain @map("src_chain") + /// ERC-20 / native token info on the source chain (stored as JSON). + srcToken Json @map("src_token") + /// Raw amount in the token's base unit (stored as string to preserve bigint precision). + srcAmount String @map("src_amount") + /// Stellar destination token info (stored as JSON). + dstToken Json @map("dst_token") + /// Minimum acceptable destination amount (base unit string). + minDstAmount String @map("min_dst_amount") + /// Best quote from solvers, populated after quoting. + quotedDstAmount String? @map("quoted_dst_amount") + /// Solver address that accepted / filled this intent. + solver String? @map("solver") + state IntentState @default(open) @map("state") + /// Unix epoch seconds. + createdAt Int @map("created_at") + /// Unix epoch seconds – intent expires after this. + deadline Int @map("deadline") + /// Unix epoch seconds – set when state becomes `filled`. + filledAt Int? @map("filled_at") + /// Actual amount received by the user (base unit string). + fillAmount String? @map("fill_amount") + /// Realized protocol fee charged on this fill (destination-token base units). + feeAmount String? @map("fee_amount") + /// On-chain transaction hash of the Stellar fill transaction. + txHash String? @map("tx_hash") + /// Unix epoch seconds – set when state becomes `slashed`. + slashedAt Int? @map("slashed_at") + slashReason String? @map("slash_reason") + /// Optimistic-concurrency version, incremented on every update (issue #405). + version Int @default(0) @map("version") + /// Caller-supplied idempotency key for POST /intents; unique across replicas (issue #404). + idempotencyKey String? @unique @map("idempotency_key") + /// Source-chain escrow deposit confirmed (issue #403); unverified intents are not fillable. + srcVerified Boolean @default(false) @map("src_verified") + /// EVM deposit transaction hash supplied at creation, if any. + srcTxHash String? @map("src_tx_hash") + /// Last verification result: status, block, received amount, detail. + srcVerification Json? @map("src_verification") + + @@index([user]) + @@index([state]) + @@index([solver]) + @@map("intents") +} + +// ─── Solver ────────────────────────────────────────────────────────────────── +// Registered solver nodes that fulfil cross-chain intents. + +model Solver { + id String @id @default(uuid()) @map("id") + /// Public key / address that uniquely identifies the solver. + address String @unique @map("address") + name String @map("name") + /// Bond amount locked in the registry contract (base-unit string). + bondAmount String @map("bond_amount") + fillsCompleted Int @default(0) @map("fills_completed") + fillsFailed Int @default(0) @map("fills_failed") + /// Cumulative filled volume (base-unit string). + totalVolume String @default("0") @map("total_volume") + /// Rolling average fill time in seconds. + avgFillTime Float @default(0) @map("avg_fill_time") + isActive Boolean @default(true) @map("is_active") + /// Unix epoch seconds. + registeredAt Int @map("registered_at") + /// Unix epoch seconds of the solver's most recent activity (registration, fill, or status change). + lastActiveAt Int @map("last_active_at") + /// Chains this solver supports (stored as JSON array of SupportedChain values). + supportedChains Json @map("supported_chains") + /// Token symbols this solver can handle. + supportedTokens Json @map("supported_tokens") + + // ── On-chain projection fields (issue #399) ─────────────────────────────── + /// Indicates the authoritative data source: "api" (REST registration) or + /// "chain" (projected from solver-registry contract events). + source String @default("api") @map("source") + /// Ledger sequence of the most recent on-chain event that updated this row. + /// NULL when source="api" (no on-chain event has been observed yet). + chainUpdatedLedger Int? @map("chain_updated_ledger") + + @@index([isActive]) + @@map("solvers") +} + +// ─── IntentAuditLog ────────────────────────────────────────────────────────── +// Append-only record of every state transition for an intent (issue #217 / #62). +// Queried by intentId to reconstruct the full history of a swap. + +model IntentAuditLog { + id BigInt @id @default(autoincrement()) @map("id") + /// FK to intents.intent_id (the user-visible UUID, not the surrogate PK). + intentId String @map("intent_id") + /// ISO-8601 / TIMESTAMPTZ of when the transition was recorded. + timestamp DateTime @default(now()) @map("timestamp") @db.Timestamptz + /// State the intent moved INTO (e.g. "cancelled", "expired", "slashed"). + toState String @map("to_state") + /// Actor who triggered the transition: a user address, solver address, or "system". + actor String @map("actor") + /// Human-readable explanation. + reason String @map("reason") + /// Optional extra data (fill amount, tx hash, deadline, …). + metadata Json? @map("metadata") + + @@index([intentId, timestamp(sort: Asc)], name: "audit_log_intent_idx") + @@map("intent_audit_log") +} + +// ─── ContractUpgrade ───────────────────────────────────────────────────────── +// Append-only history of detected WASM upgrades of the contracts the backend +// writes to (issue #402). Written by ContractVersionService when a poll sees +// a new hash or an upgrade event is ingested. + +model ContractUpgrade { + id BigInt @id @default(autoincrement()) @map("id") + /// "settlement" | "solverRegistry" + contractName String @map("contract_name") + contractId String @map("contract_id") + previousWasmHash String? @map("previous_wasm_hash") + wasmHash String? @map("wasm_hash") + /// ABI version the new hash maps to; null when unsupported. + abiVersion String? @map("abi_version") + /// "poll" | "event" + source String @map("source") + ledger Int? @map("ledger") + txHash String? @map("tx_hash") + detectedAt DateTime @default(now()) @map("detected_at") @db.Timestamptz + + @@index([contractId, detectedAt], name: "contract_upgrades_contract_idx") + @@map("contract_upgrades") +} + +// ─── Token ─────────────────────────────────────────────────────────────────── +// Static registry of tokens the protocol supports. +// Kept separate so it can be updated without migrations when the token list changes. + +model Token { + id String @id @default(uuid()) @map("id") + /// Contract address (EVM hex address or Stellar contract ID). + address String @map("address") + symbol String @map("symbol") + name String @map("name") + decimals Int @map("decimals") + chain SupportedChain @map("chain") + logoUri String? @map("logo_uri") + /// Latest known USD price (nullable – updated by a price-feed worker). + priceUsd Float? @map("price_usd") + /// Whether this is a destination-side Stellar token. + isStellar Boolean @default(false) @map("is_stellar") + /// active tokens are listed; paused stay listed but are not offered for new + /// intents; delisted are hidden from discovery and still readable by id. + status TokenStatus @default(active) @map("status") + /// evm | stellar-sac | stellar-classic. String so classic and SAC stay distinct + /// without a second database enum. + assetKind String @default("evm") @map("asset_kind") + + @@unique([address, chain]) + @@index([chain]) + @@index([symbol]) + @@map("tokens") +} + +// ─── KillSwitch (issue #477) ───────────────────────────────────────────────── +// Hierarchical emergency pause. One row per (scope, chain, token, operation) +// tuple; activating the row closes that scope for the gated operation. +// +// Because the unique key is the full scope tuple, "resume" is an idempotent +// state flip on a single row rather than a delete/insert race between replicas. +// +// `scope` is stored alongside the nullable chain/token/operation columns so the +// row is self-describing; the application maintains that invariant in one place +// (KillSwitchService.normaliseScope) rather than trusting callers. + +model KillSwitch { + id String @id @default(uuid()) @map("id") + scope KillSwitchScope @map("scope") + /// NULL for global scope. + chain String? @map("chain") + /// NULL for global and chain scopes. + token String? @map("token") + /// NULL unless scope = operation. + operation KillSwitchOperation? @map("operation") + + /// Canonical encoding of (scope, chain, token, operation), e.g. + /// "operation|stellar|USDC|fill". Postgres will not let Prisma build a + /// findUnique over nullable columns, so this non-null surrogate carries the + /// uniqueness guarantee while the typed columns above stay queryable. + scopeKey String @unique @map("scope_key") + + /// true = writes are blocked for this scope, false = explicitly resumed. + active Boolean @map("active") + /// Machine-readable reason surfaced to clients as `reason` in the 503 body. + reasonCode String @map("reason_code") + /// Free-text operator explanation (incident ticket, etc). + reason String @map("reason") + /// Operator identity that activated or resumed the switch. + activatedBy String @map("activated_by") + + /// Unix epoch ms of the last state change. Used as the cheap change-detection + /// key for the polling fallback (monotonic: re-pausing bumps it). + updatedAt Int @map("updated_at") + + createdAt Int @map("created_at") + /// Unix epoch ms of the last time this switch transitioned active -> inactive. + /// Unset while never resumed, so a brand-new row cannot inherit a stale + /// cooldown from a previous pause of the same scope. + lastResumedAt Int? @map("last_resumed_at") + + // Resume requires two distinct approvals; see KillSwitchApproval. + approvals KillSwitchApproval[] + /// Number of distinct approvals still required before the switch may resume. + approvalsRequired Int @default(2) @map("approvals_required") + + @@index([active]) + @@index([scope, chain, token, operation]) + /// Backs the polling fallback's "max(updated_at) since last snapshot?" probe. + @@index([updatedAt]) + @@map("kill_switches") +} +// ─── KillSwitchApproval (issue #477) ────────────────────────────────────────── +// One row per operator approval to resume a switch. The resume guard is a +// count of DISTINCT approver over this table, so a single operator cannot +// approve twice, and two different operators can never be one person unless the +// caller lies about identity (the API ties identity to the operator token). + +model KillSwitchApproval { + id String @id @default(uuid()) @map("id") + killSwitchId String @map("kill_switch_id") + killSwitch KillSwitch @relation(fields: [killSwitchId], references: [id], onDelete: Cascade) + /// Operator identity that granted this approval. Unique per switch so one + /// operator cannot satisfy the two-approval rule alone. + approver String @map("approver") + /// Unix epoch ms. + approvedAt Int @map("approved_at") + /// Optional note explaining the approval. + note String? @map("note") + + @@unique([killSwitchId, approver]) + @@index([killSwitchId]) + @@map("kill_switch_approvals") +} + +// ─── TreasurySnapshot ──────────────────────────────────────────────────────── +// Daily snapshots of expected vs actual treasury balances per asset. +// Used for reconciliation and historical reporting. + +model TreasurySnapshot { + id BigInt @id @default(autoincrement()) @map("id") + /// Date of the snapshot (YYYY-MM-DD). + snapshotDate String @map("snapshot_date") + /// Asset identifier (contract address or asset code). + asset String @map("asset") + /// Expected balance from fee ledger + slashes - refunds (base-unit string). + expectedBalance String @map("expected_balance") + /// Actual on-chain balance (base-unit string). + actualBalance String @map("actual_balance") + /// Difference (actualBalance - expectedBalance, base-unit string). + discrepancy String @map("discrepancy") + /// Tolerance threshold used for this check (base-unit string). + toleranceThreshold String @map("tolerance_threshold") + /// Whether abs(discrepancy) > toleranceThreshold. + hasUnexplainedDiscrepancy Boolean @map("has_unexplained_discrepancy") + /// Human-readable explanation of differences (in-flight settlements, refunds, etc). + explanation String? @map("explanation") + /// ISO-8601 / TIMESTAMPTZ of when the snapshot was taken. + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + /// Breakdown of expected balance sources (fees, slashes, refunds). + breakdown Json? @map("breakdown") + + @@unique([snapshotDate, asset], name: "snapshot_date_asset_unique") + @@index([snapshotDate]) + @@index([hasUnexplainedDiscrepancy]) + @@map("treasury_snapshots") +} + +// ─── FeeLedger ─────────────────────────────────────────────────────────────── +// Append-only ledger of all fee accruals from filled intents. + +model FeeLedger { + id BigInt @id @default(autoincrement()) @map("id") + /// FK to intents.intent_id. + intentId String @map("intent_id") + /// Asset identifier (contract address or asset code). + asset String @map("asset") + /// Fee amount in base units (string). + amount String @map("amount") + /// ISO-8601 / TIMESTAMPTZ of when the fee was accrued. + accrualAt DateTime @default(now()) @map("accrual_at") @db.Timestamptz + /// Optional transaction hash. + txHash String? @map("tx_hash") + + @@index([intentId]) + @@index([asset, accrualAt]) + @@map("fee_ledger") +} + +// ─── SlashLedger ───────────────────────────────────────────────────────────── +// Append-only ledger of all slash proceeds from solver penalties. + +model SlashLedger { + id BigInt @id @default(autoincrement()) @map("id") + /// Solver address that was slashed. + solverAddress String @map("solver_address") + /// Asset identifier (contract address or asset code). + asset String @map("asset") + /// Slashed amount in base units (string). + amount String @map("amount") + /// ISO-8601 / TIMESTAMPTZ of when the slash occurred. + slashedAt DateTime @default(now()) @map("slashed_at") @db.Timestamptz + /// Reason for the slash. + reason String @map("reason") + /// Optional transaction hash. + txHash String? @map("tx_hash") + + @@index([solverAddress]) + @@index([asset, slashedAt]) + @@map("slash_ledger") +} + +// ─── RefundLedger ──────────────────────────────────────────────────────────── +// Append-only ledger of all refunds issued to users. + +model RefundLedger { + id BigInt @id @default(autoincrement()) @map("id") + /// FK to intents.intent_id. + intentId String @map("intent_id") + /// User address that received the refund. + userAddress String @map("user_address") + /// Asset identifier (contract address or asset code). + asset String @map("asset") + /// Refund amount in base units (string). + amount String @map("amount") + /// ISO-8601 / TIMESTAMPTZ of when the refund was issued. + issuedAt DateTime @default(now()) @map("issued_at") @db.Timestamptz + /// Reason for the refund. + reason String @map("reason") + /// Optional transaction hash. + txHash String? @map("tx_hash") + + @@index([intentId]) + @@index([userAddress]) + @@index([asset, issuedAt]) + @@map("refund_ledger") +} + +// ─── AdminAuditLog ─────────────────────────────────────────────────────────── +// Append-only record of privileged operator and governance actions: feature +// flag changes (issue #495), kill-switch toggles and guardian overrides (#507). + +model AdminAuditLog { + id BigInt @id @default(autoincrement()) @map("id") + /// Admin principal id, or "guardian" / "system" for automated actions. + actor String @map("actor") + /// Dotted action name, e.g. "flag.update", "guardian.override". + action String @map("action") + /// Target of the action, e.g. "flag:onchain-dry-run". + target String @map("target") + before Json? @map("before") + after Json? @map("after") + reason String? @map("reason") + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + + @@index([target, createdAt(sort: Desc)]) + @@map("admin_audit_log") +} + +// ─── FeatureFlag ───────────────────────────────────────────────────────────── +// Runtime feature flags (issue #495). `rules` is an ordered JSON array of +// { value, percentage?, solvers?, chains? }; the first matching rule wins. + +model FeatureFlag { + key String @id @map("key") + defaultValue Boolean @map("default_value") + rules Json @map("rules") + version Int @default(1) @map("version") + updatedBy String @map("updated_by") + updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz + + @@map("feature_flags") +} + +// Pending flag changes that need a second approver (e.g. dry-run off in production). +model FlagChangeRequest { + id String @id @default(uuid()) @map("id") + flagKey String @map("flag_key") + /// Proposed { defaultValue, rules }. + proposed Json @map("proposed") + proposedBy String @map("proposed_by") + /// Admin ids that approved, proposer first. + approvals String[] @map("approvals") + /// pending | applied + status String @default("pending") @map("status") + reason String? @map("reason") + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + appliedAt DateTime? @map("applied_at") @db.Timestamptz + + @@index([flagKey, status]) + @@map("flag_change_requests") +} + +// ─── GuardianAction ────────────────────────────────────────────────────────── +// Emergency actions ingested from the on-chain guardian contract (issue #507). +// One row per activating event; cleared by the matching guardian event or a +// superadmin override. + +model GuardianAction { + /// Soroban event id of the activating event. + id String @id @map("id") + /// pause | freeze | blacklist + kind String @map("kind") + /// Frozen parameter key or blacklisted solver address; "" for pause. + target String @map("target") + active Boolean @map("active") + txHash String @map("tx_hash") + ledger Int @map("ledger") + activatedAt DateTime @map("activated_at") @db.Timestamptz + clearedAt DateTime? @map("cleared_at") @db.Timestamptz + clearedTxHash String? @map("cleared_tx_hash") + overriddenBy String? @map("overridden_by") + + @@index([active]) + @@map("guardian_actions") +} + +// ─── OnchainOutbox ─────────────────────────────────────────────────────────── +// Transactional outbox for on-chain writes (issue #396). A row is inserted in +// the same database transaction as the intent mutation it mirrors, and +// OutboxRelayService submits it to Soroban afterwards — so the DB can never say +// "accepted" while the corresponding transaction silently never went out. +// +// `id` is a monotonically increasing sequence and doubles as the per-intent +// ordering key: a row is only claimable once every earlier row for the same +// intent has reached a terminal success state. + +enum OutboxStatus { + pending + processing + submitted + confirmed + simulated + dead +} + +model OnchainOutbox { + id BigInt @id @default(autoincrement()) @map("id") + /// intents.intent_id this operation belongs to (ordering partition key). + intentId String @map("intent_id") + /// Contract operation, e.g. "create_intent", "accept_intent". + operation String @map("operation") + /// Operation arguments (JSON-safe, bigint amounts as strings). + payload Json @map("payload") + status OutboxStatus @default(pending) @map("status") + /// Number of times this row has been claimed by a relay worker. + attempts Int @default(0) @map("attempts") + nextAttemptAt DateTime @default(now()) @map("next_attempt_at") @db.Timestamptz + /// Lease expiry while status = processing; a crashed worker's row is reclaimed after this. + lockedUntil DateTime? @map("locked_until") @db.Timestamptz + /// Hash of the signed envelope, persisted BEFORE submission (crash idempotency). + envelopeHash String? @map("envelope_hash") + /// Hash of the transaction that was submitted. + txHash String? @map("tx_hash") + lastError String? @map("last_error") + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz + + @@index([status, nextAttemptAt], name: "onchain_outbox_claim_idx") + @@index([intentId, id], name: "onchain_outbox_intent_order_idx") + @@map("onchain_outbox") +} + +// ─── PendingSlash ──────────────────────────────────────────────────────────── +// Durable saga state for a solver slash (issue #397): +// detected → challenge_window → submitted → confirmed | cancelled +// The unique constraint on intent_id enforces exactly-once slashing per intent. + +enum PendingSlashState { + detected + challenge_window + submitted + confirmed + cancelled +} + +model PendingSlash { + id String @id @default(uuid()) @map("id") + intentId String @unique @map("intent_id") + solverAddress String @map("solver_address") + reason String @map("reason") + state PendingSlashState @default(detected) @map("state") + /// The intent's fill deadline (unix seconds) that the solver missed. + fillDeadline Int @map("fill_deadline") + detectedAt DateTime @map("detected_at") @db.Timestamptz + /// Slash may not be submitted before this instant. + challengeEndsAt DateTime @map("challenge_ends_at") @db.Timestamptz + attempts Int @default(0) @map("attempts") + nextAttemptAt DateTime @default(now()) @map("next_attempt_at") @db.Timestamptz + /// Lease held by the pipeline worker that is currently verifying/submitting. + lockedUntil DateTime? @map("locked_until") @db.Timestamptz + txHash String? @map("tx_hash") + /// true when the registry client simulated but did not broadcast (dry-run / gated submit). + simulated Boolean @default(false) @map("simulated") + submittedAt DateTime? @map("submitted_at") @db.Timestamptz + confirmedAt DateTime? @map("confirmed_at") @db.Timestamptz + cancelledAt DateTime? @map("cancelled_at") @db.Timestamptz + cancelReason String? @map("cancel_reason") + cancelledBy String? @map("cancelled_by") + /// Fill transaction that cancelled the slash, when cancelled by a fill proof. + fillTxHash String? @map("fill_tx_hash") + lastError String? @map("last_error") + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz + + @@index([state, challengeEndsAt], name: "pending_slashes_due_idx") + @@index([solverAddress], name: "pending_slashes_solver_idx") + @@map("pending_slashes") +} diff --git a/src/app.module.ts b/src/app.module.ts index e69de29b..d050f8b1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -0,0 +1,89 @@ +import { Module } from "@nestjs/common"; +import { APP_GUARD } from "@nestjs/core"; +import { ThrottlerModule } from "@nestjs/throttler"; +import { ScheduleModule } from "@nestjs/schedule"; +import { ConfigModule } from "./config/config.module"; +import { HealthModule } from "./health/health.module"; +import { TokensModule } from "./tokens/tokens.module"; +import { IntentsModule } from "./intents/intents.module"; +import { MetricsModule } from "./metrics/metrics.module"; +import { SolversModule } from "./solvers/solvers.module"; +import { StatsModule } from "./stats/stats.module"; +import { SorobanModule } from "./soroban/soroban.module"; +import { RoutingModule } from "./routing/routing.module"; +import { KillSwitchModule } from "./killswitch/killswitch.module"; +import { PrismaModule } from "./prisma/prisma.module"; +import { TreasuryModule } from "./treasury/treasury.module"; +import { GovernanceModule } from "./governance/governance.module"; +import { LeaderElectionModule } from "./common/leader-election"; +import { AdminModule } from "./admin/admin.module"; +import { JobsModule } from "./jobs/jobs.module"; +import { FlagsModule } from "./flags/flags.module"; +import { GuardianStateModule } from "./governance/guardian-state.service"; +import { DatasetsModule } from "./datasets/datasets.module"; +import { AbuseModule } from "./abuse/abuse.module"; +import { ApiKeysModule } from "./auth/api-keys/api-keys.module"; +import { TieredThrottleGuard } from "./auth/rate-limit/tiered-throttle.guard"; + +@Module({ + imports: [ + // ThrottlerModule stays for the per-user intent guard (#45) and the + // per-route @Throttle decorators (quote). The GLOBAL IP throttle it used + // to provide is replaced by the tiered, distributed TieredThrottleGuard + // (issue #441) — see the APP_GUARD provider below. + ThrottlerModule.forRoot([ + { + name: "global", + ttl: 60_000, // ms + limit: 100, + }, + ]), + // Enable scheduled tasks (cron jobs) + ScheduleModule.forRoot(), + ConfigModule, + PrismaModule, + // Issue #441 — API key tiers + distributed rate limiting. + ApiKeysModule, + // @Global() — registers MetricsService / MetricsInterceptor / MetricsController + // for the whole app. Must be imported once in the root module or the global + // providers never become visible to other modules (e.g. IntentsSweeperService) + // and Nest fails to resolve MetricsService at boot. + MetricsModule, + // Emergency pause control plane (issue #477). @Global() so KillSwitchGuard + // can gate write handlers in any module. + KillSwitchModule, + // Leader election must be initialised before any worker module so that + // LeaderElectionService is available when workers call registerWorker() + // in their onModuleInit hooks. + LeaderElectionModule.forRoot(), + // Issues #494/#495/#507 — admin RBAC + audit, job queue, runtime flags, + // guardian-derived policy state. + AdminModule, + JobsModule, + FlagsModule, + GuardianStateModule, + AbuseModule, + HealthModule, + TokensModule, + IntentsModule, + SolversModule, + StatsModule, + SorobanModule, + RoutingModule, + TreasuryModule, + GovernanceModule, + ], + controllers: [], + providers: [ + // Issue #441 — tiered, distributed rate limit replaces the legacy + // per-process global IP throttle. Anonymous requests are limited to the + // `public` tier (100/min per IP — unchanged); API-key requests are limited + // by their tier. Enforced by the Redis-backed DistributedRateLimiter with + // a bounded local fallback, so a Redis outage never means unlimited. + { + provide: APP_GUARD, + useClass: TieredThrottleGuard, + }, + ], +}) +export class AppModule {} diff --git a/src/common/stellar-signature.ts b/src/common/stellar-signature.ts index e69de29b..20fc23be 100644 --- a/src/common/stellar-signature.ts +++ b/src/common/stellar-signature.ts @@ -0,0 +1,179 @@ +/** + * Stellar keypair signature verification helper. + * + * Convention used throughout this project: + * message = the canonical string that was signed + * signature = base64-encoded 64-byte Ed25519 signature produced by + * Keypair.sign(Buffer.from(message)) + * + * The signer proves control of `publicKey` by supplying a valid signature + * over the message. We never trust a caller-supplied address alone. + */ +import { Keypair } from "@stellar/stellar-sdk"; +import { UnauthorizedException } from "@nestjs/common"; +import { createHash } from "node:crypto"; + +export const INTENT_SIGNATURE_CLOCK_SKEW_SECONDS = 30; +export const MAX_INTENT_SIGNATURE_TTL_SECONDS = 900; + +export interface IntentSignatureContext { + network: string; + nonce: string; + expiresAt: number; +} + +function canonicalPayload(payload: Record): string { + return JSON.stringify( + Object.fromEntries(Object.entries(payload).sort(([left], [right]) => left.localeCompare(right))), + ); +} + +function buildV2IntentMessage( + context: IntentSignatureContext, + action: "accept" | "fill" | "cancel", + /** + * Build the canonical message that a solver must sign to update their mutable + * profile fields (name / supportedChains / supportedTokens / avgFillTime). + intentId: string, + payload: Record, +): string { + const payloadHash = createHash("sha256").update(canonicalPayload(payload), "utf8").digest("hex"); + return `vortex:${context.network}:${action}:${intentId}:${context.nonce}:${context.expiresAt}:${payloadHash}`; +} + +/** + * Verify that `signature` (base64) over `message` (utf-8) was produced by + * the private key corresponding to `publicKey` (Stellar G-address). + * + * Throws UnauthorizedException on any failure so callers can let it propagate + * straight to the HTTP layer. + */ +export function verifyStellarSignature( + publicKey: string, + message: string, + signature: string, +): void { + try { + const keypair = Keypair.fromPublicKey(publicKey); + const messageBytes = Buffer.from(message, "utf8"); + const sigBytes = Buffer.from(signature, "base64"); + const valid = keypair.verify(messageBytes, sigBytes); + if (!valid) { + throw new UnauthorizedException("Signature verification failed"); + } + } catch (err) { + if (err instanceof UnauthorizedException) throw err; + // Invalid public key, bad base64, etc. + throw new UnauthorizedException("Invalid signature or public key"); + } +} + +/** + * Build the canonical message that a user must sign to cancel an intent. + */ +export function buildCancelMessage(intentId: string, context?: IntentSignatureContext, user?: string): string { + if (context) return buildV2IntentMessage(context, "cancel", intentId, { user: user ?? "" }); + return `cancel:${intentId}`; +} + +/** + * Build the canonical message that a solver must sign to authenticate its WS connection. + */ +export function buildWsAuthMessage(solver: string, timestamp: number | string): string { + return `solver-auth:${solver}:${String(timestamp)}`; +} + +/** + * Build the canonical message that a solver must sign to accept an intent. + */ +export function buildAcceptMessage(intentId: string, solver: string, context?: IntentSignatureContext): string { + if (context) return buildV2IntentMessage(context, "accept", intentId, { solver }); + return `accept:${intentId}:${solver}`; +} + +/** + * Build the canonical message that a solver must sign to fill an intent. + */ +export function buildFillMessage( + intentId: string, + solver: string, + context?: IntentSignatureContext, + fill?: { fillAmount: string; txHash?: string }, +): string { + if (context) { + return buildV2IntentMessage(context, "fill", intentId, { + solver, + fillAmount: fill?.fillAmount ?? "", + txHash: fill?.txHash ?? null, + }); + } + return `fill:${intentId}:${solver}`; +} + +/** + * Build the canonical message that a solver must sign to register. + */ +export function buildRegisterMessage(address: string): string { + return `register:${address}`; +} + +/** + * Build the canonical message that a solver must sign to change status. + */ +export function buildSolverStatusMessage(action: "deactivate" | "reactivate" | "deregister", address: string): string { + return `${action}:${address}`; +} + +/** + * Build the canonical message that a solver must sign to update its own + * mutable profile fields (name / supportedChains / supportedTokens / + * avgFillTime — issue #273, `PATCH /api/v1/solvers/:address`). + * + * Signing over just the address is sufficient here: it proves control of the + * account whose profile is being edited, and the request body is already + * constrained by the DTO whitelist so no immutable field can ride along. + */ +export function buildUpdateSolverMessage(address: string): string { + return `update-solver:${address}`; +} + +/** + * Build the canonical message that a solver must sign to submit a slash dispute. + */ +export function buildDisputeMessage(slashId: string, address: string, reason: string): string { + return `dispute:${slashId}:${address}:${reason}`; +} + +/** + * Build the canonical message a reviewer must sign to move a dispute into review. + */ +export function buildDisputeReviewMessage(disputeId: string): string { + return `dispute-review:${disputeId}`; +} + +/** + * Build the canonical message a reviewer must sign to decide a dispute. + */ +export function buildDisputeDecisionMessage(disputeId: string, resolution: string, reason: string): string { + return `dispute-decision:${disputeId}:${resolution}:${reason}`; +} + +/** + * Build the canonical message that a solver must sign to update their mutable + * profile fields (name / supportedChains / supportedTokens / avgFillTime). + * + * Signing over just the address is sufficient here: it proves control of the + * account whose profile is being edited, and the request body is already + * constrained by the DTO whitelist so no immutable field can ride along. + */ +export function buildUpdateSolverMessage(address: string): string { + return `update-solver:${address}`; +} + +/** + * Canonical message a solver signs to prove a fill landed in time and cancel + * a pending slash during its challenge window (issue #397). + */ +export function buildFillProofMessage(intentId: string, solver: string, txHash: string): string { + return `fill-proof:${intentId}:${solver}:${txHash}`; +} diff --git a/src/config/configuration.ts b/src/config/configuration.ts index e69de29b..82819fe0 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -0,0 +1,529 @@ +export type FeePercentile = + | "min" + | "mode" + | "p10" + | "p20" + | "p30" + | "p40" + | "p50" + | "p60" + | "p70" + | "p80" + | "p90" + | "p95" + | "p99" + | "max"; + +/** + * Default open-intent deadline in seconds per source chain. + * + * Controls how long after creation an intent can be accepted by a solver. + * Values are intentionally generous — chains with slower finality get more + * time so solvers can confidently assess liquidity before committing. + */ +export const CHAIN_DEADLINE_DEFAULTS: Record = { + stellar: 900, // ~15 min — fast finality + base: 1800, // ~30 min + optimism: 1800, + arbitrum: 1800, + ethereum: 3600, // ~1 hr — slower finality + polygon: 2700, // ~45 min + avalanche: 1800, +}; + +/** Fallback open-intent deadline when chain is not in the map. */ +export const DEFAULT_DEADLINE_SECONDS = 1800; + +/** + * Per-chain fill-window in seconds: the time a solver has from accept to fill. + * + * Design rationale + * ──────────────── + * The fill window is intentionally shorter than the full open-intent deadline + * (CHAIN_DEADLINE_DEFAULTS) because accept-to-fill should always be a strict + * subset of the total time budget. Values are chosen to give solvers + * realistic execution time on each chain while keeping the slashing window + * fair: + * + * stellar 120 s — 5-second ledger time; a solver has plenty of margin. + * base 600 s — 2-second blocks; ~5-min window comfortable for bridging. + * optimism 600 s — same as Base (same block cadence). + * arbitrum 600 s — sub-second blocks but finality waits for L1 batch. + * ethereum 1800 s — 12-second slots + confirmation depth = larger window. + * polygon 900 s — ~2-second blocks; moderate finality. + * avalanche 600 s — 1-2 second finality; similar profile to Base/Optimism. + * + * These defaults can be overridden at deploy-time via the corresponding + * FILL_WINDOW_ environment variables (e.g. FILL_WINDOW_ETHEREUM=3600), + * following the same override mechanism as CHAIN_DEADLINE_DEFAULTS. + * They are intentionally not exposed as AppConfig fields — like + * CHAIN_DEADLINE_DEFAULTS they are module-level constants that callers import + * directly, keeping configuration.ts the single source of truth without + * forcing every consumer to inject ConfigService for a plain number lookup. + */ +export const CHAIN_FILL_WINDOW_DEFAULTS: Record = { + stellar: 120, // 2 min — fast finality; solver has ample time + base: 600, // 10 min + optimism: 600, // 10 min + arbitrum: 600, // 10 min — L1 batch delay makes this realistic + ethereum: 1800, // 30 min — slower slot + confirmation depth + polygon: 900, // 15 min + avalanche: 600, // 10 min — fast finality, bridge latency dominates +}; + +/** Fallback fill-window when chain is not in the map. */ +export const DEFAULT_FILL_WINDOW_SECONDS = 600; + +/** + * Stellar network passphrases keyed by the `STELLAR_NETWORK` values the schema + * accepts. + * + * A transaction envelope is only valid for the network it was built for, so + * anything that assembles an envelope — today only the shadow-mode + * simulation path in `StellarTxService.simulateContract` — needs this map. + * It lives next to the other network-derived constants rather than in the + * service so there is exactly one place to look when a network is added. + * + * Lookups fall back to testnet (see `StellarTxService`'s constructor): the + * worst outcome for a *simulated* envelope is a simulation against the wrong + * network, which surfaces immediately as a divergence rather than as a silent + * wrong-network write, because simulation never broadcasts. + */ +export const NETWORK_PASSPHRASES: Record = { + testnet: "Test SDF Network ; September 2015", + futurenet: "Test SDF Future Network ; October 2022", + mainnet: "Public Global Stellar Network ; September 2015", +}; + +export interface AppConfig { + nodeEnv: string; + port: number; + databaseUrl: string; + datasets: import("../datasets/datasets.types").DatasetsConfig; + stellar: { + network: "testnet" | "futurenet" | "mainnet"; + sorobanRpcUrl: string; + horizonUrl: string; + settlementContractId: string; + solverRegistryContractId: string; + signerSecretKey: string; + // Secret key for the backend's Soroban signer. Empty outside production + // (no on-chain write path exists yet); envValidationSchema requires and + // format-checks it in production so it can never silently fall back to + // a placeholder. Never log this value. + signingKey: string; + /** Fee percentile to use when estimating Soroban inclusion fees. */ + feePercentile: FeePercentile; + /** Maximum fee in stroops for fee-bump escalation (#388). */ + maxFeeStroops: number; + /** Number of channel accounts in the pool (#387). */ + channelPoolSize: number; + /** Comma-separated list of channel account secret keys (#387). */ + channelSecretKeys: string; + }; + treasury: { + address: string; + }; + onchainIntentsEnabled: boolean; + legacyStellarSignatures: boolean; + evm: { + rpcAllowlist: string[]; + chains: Record< + "ethereum" | "base" | "polygon" | "arbitrum" | "optimism" | "avalanche", + { chainId: number; rpcUrl: string; escrowAddress: string } + >; + }; + intentRetentionDays: number; + intentRetentionSweepMs: number; + /** + * Dry-run flag for on-chain write paths (issue #260). + * + * When true every write path (invokeContract, slashSolver) simulates and + * logs but never broadcasts a transaction. Defaults to true outside + * production; must be explicitly set in production (validated by + * envValidationSchema — see src/config/env.validation.ts). + * + * This value is the env default. At runtime the `onchain-dry-run` feature + * flag (src/flags/, issue #495) can override it without a restart — see + * docs/runbooks/onchain-cutover.md for the staged rollout procedure. + */ + onchainDryRun: boolean; + corsOrigin: string; + /** Maximum concurrent WebSocket connections (0 = unlimited). */ + wsMaxConnections: number; + wsBackplane: "memory" | "redis"; + redisUrl: string; + + // ── Resource-exhaustion limits (issue #476) ─────────────────────────────── + /** Maximum JSON nesting depth accepted by the body parser middleware. */ + jsonMaxDepth: number; + /** Maximum chain-filter values in a single WS subscribe message. */ + wsMaxFilterChains: number; + /** Maximum concurrent active subscriptions per WS connection. */ + wsMaxSubscriptions: number; + /** Default Postgres statement_timeout (ms) for standard route queries. */ + dbQueryTimeoutMs: number; + /** Postgres statement_timeout (ms) for batch-lookup queries. */ + dbBatchQueryTimeoutMs: number; + /** Postgres statement_timeout (ms) for stats/aggregate queries. */ + dbStatsQueryTimeoutMs: number; + + // ── Emergency kill-switch (issue #477) ───────────────────────────────────── + killswitch: { + /** + * Shared secret for the operator control plane (`/api/v1/ops/killswitch`). + * Empty disables those routes entirely — the control plane is never open. + */ + operatorToken: string; + /** + * Redis URL used for cross-replica pause propagation. Empty falls back to + * database polling only, which still meets the propagation budget. + */ + redisUrl: string; + /** + * Interval (ms) for the `max_updated_at` probe that backstops Redis pub/sub. + * Worst-case propagation delay is roughly this value, so it must stay + * comfortably under the 5 s propagation requirement. + */ + pollMs: number; + }; + + /** + * Shadow-mode divergence monitor (issue #401). + * + * Runs read-only on-chain simulations of every intent state transition in + * parallel with the authoritative off-chain path and reports where the two + * disagree. See docs/runbooks/onchain-cutover.md for the go/no-go threshold. + */ + shadow: { + /** Master switch. When false, `ShadowService.observe` is a no-op. */ + enabled: boolean; + /** Fraction of transitions to simulate, in `[0, 1]`. `1` = every one. */ + sampleRate: number; + /** Hard cap on queued observations; beyond this they are dropped + counted. */ + queueMax: number; + /** How many observations the background drain simulates concurrently. */ + concurrency: number; + /** + * Public key used as the source account for simulation envelopes. + * + * Never signed, never submitted, never charged — it only has to be a valid + * StrKey. Empty means "simulate nothing", which the monitor reports as + * `contract_unconfigured` rather than as zero divergence. + */ + sourceAccount: string; + }; + + governance: { + /** + * On-chain governance / parameters contract ID. + * When set, ProtocolParamsService reads current + scheduled parameters + * from this contract and exposes them via GET /api/v1/params. + * Leave blank to use code / env defaults only. + */ + paramsContractId: string; + /** + * How often (in milliseconds) to poll the parameters contract for changes. + * Default: 30 000 ms (30 s). + */ + paramsPollIntervalMs: number; + }; + + leaderElection: { + /** When false, all workers run unconditionally (pre-election behaviour). */ + enabled: boolean; + /** Heartbeat interval in ms (default 5000). */ + heartbeatMs: number; + }; + /** + * Process role (issue #494). Producers may enqueue jobs from any role; + * queue workers only run when the role is "worker" or "all". + */ + processRole: "api" | "worker" | "all"; + jobs: { + /** "memory" (single-process, dev/test) or "bullmq" (Redis-backed, durable). */ + driver: "memory" | "bullmq"; + /** Grace period for in-flight jobs on shutdown before they are returned to the queue. */ + shutdownTimeoutMs: number; + }; + flags: { + /** Cross-instance change propagation: in-process only, or Redis pub/sub (issue #495). */ + pubsub: "memory" | "redis"; + /** Safety-net reload interval for the flag cache, in ms. */ + refreshMs: number; + /** Hard pins that win over DB state, e.g. "onchain-dry-run=true". */ + overrides: string; + }; + /** Raw ADMIN_API_KEYS value ("id:role:secret,..."); parsed by src/admin/admin-auth.ts. */ + adminApiKeys: string; + /** Soroban contract emitting guardian emergency events (issue #507). Empty disables ingestion. */ + guardianContractId: string; + /** Addresses (users and solvers) owned by the synthetic canary (issue #496). */ + canaryAddresses: string[]; + /** Public anonymised dataset publication settings (see docs/rfcs/0001). */ + datasets: { + enabled: boolean; + anonymize: boolean; + salt: string; + saltRotationHours: number; + saltRetentionWindows: number; + publicBucket: string; + storageKind: "local" | "memory"; + localDir: string; + }; + secrets: { + /** Provider name: "env" | "aws-secrets-manager" | "vault-kv". */ + provider: "env" | "aws-secrets-manager" | "vault-kv"; + /** Poll interval for secret rotation (ms). */ + refreshIntervalMs: number; + /** Comma-separated extra secrets: "name:envVar:required". */ + extra: string; + /** WS gateway hardening (issue #455). */ + ws: { + /** Largest inbound frame accepted; larger frames close the socket (1009). */ + maxPayloadBytes: number; + /** Concurrent connections allowed from one client IP (0 = unlimited). */ + maxConnectionsPerIp: number; + /** Reverse-proxy hops to trust when reading X-Forwarded-For (0 = use the socket address). */ + trustProxyHops: number; + /** Inbound token bucket: sustained messages per second and burst size. */ + rateLimitPerSec: number; + rateLimitBurst: number; + /** Rate-limited messages tolerated before the connection is closed (1008). */ + rateLimitMaxViolations: number; + /** Messages held for a slow consumer before the slow-consumer policy applies. */ + outboundQueueMax: number; + /** Socket bufferedAmount above which further messages are queued instead of sent. */ + outboundBufferBytes: number; + slowConsumerPolicy: "drop_oldest" | "disconnect"; + /** Drain timeout for graceful shutdown (Activity 2). */ + drainTimeoutMs: number; + }; + /** HS256 secret for solver JWTs (SEP-10 auth, #442); empty disables JWT auth. */ + authJwtSecret: string; + /** + * How often (ms) the local rate-limiter fallback prunes expired window + * entries (issue #441). Only relevant during a Redis outage. + */ + rateLimitLocalPruneMs: number; + /** + * Redis URL backing the distributed rate limiter (issue #441). Empty means + * "local bounded limiter only" — the limit is still enforced, just per + * process. Defaults to `REDIS_URL` when that is set. + */ + rateLimitRedisUrl: string; + /** + * Cross-replica transport for solver-credential revocation invalidation + * (issue #443): "memory" (single instance) or "redis" (pub/sub). + */ + credentialRevocationPubsub: "memory" | "redis"; + /** SSE intent feed (issue #433). */ + sse: { + /** Heartbeat interval in milliseconds (SSE comment frames). */ + heartbeatMs: number; + /** Maximum buffered output bytes per SSE client before it is disconnected. */ + maxBufferBytes: number; + }; + /** Health probes (issue #492). */ + health: { + /** Roles this process serves; readiness requires every indicator critical to any of them. */ + roles: Array<"api" | "ws" | "worker">; + /** Background re-check interval; probes only read cached results. */ + checkIntervalMs: number; + /** Consecutive failed evaluations before readiness turns false. */ + readyFailureThreshold: number; + /** Consecutive passing evaluations before readiness turns true again. */ + readySuccessThreshold: number; + /** Event-loop delay above which liveness fails. */ + eventLoopMaxLagMs: number; + /** Soroban RPC endpoints probed for quorum (majority must be healthy). */ + rpcHealthUrls: string[]; + }; +} + +export default (): AppConfig => ({ + nodeEnv: process.env.NODE_ENV ?? "development", + port: parseInt(process.env.PORT ?? "4000", 10), + databaseUrl: + process.env.DATABASE_URL ?? + "postgresql://vortex:vortex@localhost:5432/vortex?schema=public", + stellar: { + network: (process.env.STELLAR_NETWORK ?? "testnet") as AppConfig["stellar"]["network"], + sorobanRpcUrl: process.env.SOROBAN_RPC_URL ?? "https://soroban-testnet.stellar.org", + horizonUrl: process.env.HORIZON_URL ?? "https://horizon-testnet.stellar.org", + settlementContractId: process.env.SETTLEMENT_CONTRACT_ID ?? "", + solverRegistryContractId: process.env.SOLVER_REGISTRY_CONTRACT_ID ?? "", + signerSecretKey: process.env.STELLAR_SIGNER_SECRET_KEY ?? "", + signingKey: process.env.SOROBAN_SIGNING_KEY ?? "", + feePercentile: (process.env.SOROBAN_FEE_PERCENTILE ?? "p50") as FeePercentile, + maxFeeStroops: parseInt(process.env.SOROBAN_MAX_FEE_STROOPS ?? "1000000", 10), + channelPoolSize: parseInt(process.env.CHANNEL_POOL_SIZE ?? "8", 10), + channelSecretKeys: process.env.CHANNEL_SECRET_KEYS ?? "", + }, + treasury: { + address: process.env.TREASURY_ADDRESS ?? "", + }, + onchainIntentsEnabled: (process.env.ONCHAIN_INTENTS_ENABLED ?? "false") === "true", + legacyStellarSignatures: + process.env.ALLOW_LEGACY_STELLAR_SIGNATURES === "true" || process.env.NODE_ENV === "test", + evm: { + rpcAllowlist: (process.env.EVM_RPC_ALLOWLIST ?? "").split(",").map((host) => host.trim()).filter(Boolean), + chains: { + ethereum: { chainId: 1, rpcUrl: process.env.ETHEREUM_RPC_URL ?? "", escrowAddress: process.env.ETHEREUM_ESCROW_ADDRESS ?? "" }, + base: { chainId: 8453, rpcUrl: process.env.BASE_RPC_URL ?? "", escrowAddress: process.env.BASE_ESCROW_ADDRESS ?? "" }, + polygon: { chainId: 137, rpcUrl: process.env.POLYGON_RPC_URL ?? "", escrowAddress: process.env.POLYGON_ESCROW_ADDRESS ?? "" }, + arbitrum: { chainId: 42161, rpcUrl: process.env.ARBITRUM_RPC_URL ?? "", escrowAddress: process.env.ARBITRUM_ESCROW_ADDRESS ?? "" }, + optimism: { chainId: 10, rpcUrl: process.env.OPTIMISM_RPC_URL ?? "", escrowAddress: process.env.OPTIMISM_ESCROW_ADDRESS ?? "" }, + avalanche: { chainId: 43114, rpcUrl: process.env.AVALANCHE_RPC_URL ?? "", escrowAddress: process.env.AVALANCHE_ESCROW_ADDRESS ?? "" }, + }, + }, + intentRetentionDays: parseInt(process.env.INTENT_RETENTION_DAYS ?? "30", 10), + intentRetentionSweepMs: parseInt(process.env.INTENT_RETENTION_SWEEP_MS ?? "60000", 10), + // Default to dry-run (true) outside production; in production the value must + // be explicitly set (validated by envValidationSchema). + onchainDryRun: process.env.ONCHAIN_DRY_RUN !== undefined + ? process.env.ONCHAIN_DRY_RUN === "true" + : process.env.NODE_ENV !== "production", + corsOrigin: process.env.CORS_ORIGIN ?? "*", + wsMaxConnections: parseInt(process.env.WS_MAX_CONNECTIONS ?? "1000", 10), + wsBackplane: (process.env.WS_BACKPLANE ?? "memory") as "memory" | "redis", + redisUrl: process.env.REDIS_URL ?? "redis://localhost:6379", + + // ── Resource-exhaustion limits (issue #476) ─────────────────────────────── + jsonMaxDepth: parseInt(process.env.JSON_MAX_DEPTH ?? "10", 10), + wsMaxFilterChains: parseInt(process.env.WS_MAX_FILTER_CHAINS ?? "20", 10), + wsMaxSubscriptions: parseInt(process.env.WS_MAX_SUBSCRIPTIONS ?? "10", 10), + dbQueryTimeoutMs: parseInt(process.env.DB_QUERY_TIMEOUT_MS ?? "5000", 10), + dbBatchQueryTimeoutMs: parseInt(process.env.DB_BATCH_QUERY_TIMEOUT_MS ?? "10000", 10), + dbStatsQueryTimeoutMs: parseInt(process.env.DB_STATS_QUERY_TIMEOUT_MS ?? "15000", 10), + + // ── Emergency kill-switch (issue #477) ───────────────────────────────────── + killswitch: { + operatorToken: process.env.KILLSWITCH_OPERATOR_TOKEN ?? "", + // Reuse the WS backplane URL when set; an explicit empty value opts out of + // Redis entirely and leaves propagation to database polling. + redisUrl: + process.env.KILLSWITCH_REDIS_URL ?? + (process.env.REDIS_URL && process.env.WS_BACKPLANE === "redis" ? process.env.REDIS_URL : ""), + // 2000 ms + request latency stays well inside the 5 s propagation budget + // even when Redis is unavailable. + pollMs: parseInt(process.env.KILLSWITCH_POLL_MS ?? "2000", 10), + }, + shadow: { + // Off by default: the monitor costs one simulation per sampled transition, + // so it is opt-in per environment rather than something a deployer + // discovers they are paying for. + enabled: (process.env.SHADOW_MODE_ENABLED ?? "false") === "true", + sampleRate: clampSampleRate(process.env.SHADOW_SAMPLE_RATE), + queueMax: clampPositiveInt(process.env.SHADOW_QUEUE_MAX, 256), + concurrency: clampPositiveInt(process.env.SHADOW_CONCURRENCY, 4), + sourceAccount: process.env.SHADOW_SOURCE_ACCOUNT ?? "", + }, + governance: { + paramsContractId: process.env.PARAMS_CONTRACT_ID ?? "", + paramsPollIntervalMs: parseInt(process.env.PARAMS_POLL_INTERVAL_MS ?? "30000", 10), + }, + leaderElection: { + enabled: (process.env.LEADER_ELECTION_ENABLED ?? "false") === "true", + heartbeatMs: parseInt(process.env.LEADER_ELECTION_HEARTBEAT_MS ?? "5000", 10), + }, + processRole: (process.env.PROCESS_ROLE ?? "all") as AppConfig["processRole"], + jobs: { + driver: (process.env.JOBS_DRIVER ?? "memory") as AppConfig["jobs"]["driver"], + shutdownTimeoutMs: parseInt(process.env.JOBS_SHUTDOWN_TIMEOUT_MS ?? "25000", 10), + }, + flags: { + pubsub: (process.env.FLAGS_PUBSUB ?? "memory") as AppConfig["flags"]["pubsub"], + refreshMs: parseInt(process.env.FLAGS_REFRESH_MS ?? "30000", 10), + overrides: process.env.FLAG_OVERRIDES ?? "", + }, + adminApiKeys: process.env.ADMIN_API_KEYS ?? "", + datasets: { + enabled: (process.env.DATASETS_ENABLED ?? "false") === "true", + anonymize: (process.env.DATASETS_ANONYMIZE ?? "true") === "true", + salt: process.env.DATASETS_SALT ?? "", + saltRotationHours: parseInt(process.env.DATASETS_SALT_ROTATION_HOURS ?? "24", 10), + saltRetentionWindows: parseInt(process.env.DATASETS_SALT_RETENTION_WINDOWS ?? "2", 10), + publicBucket: process.env.DATASETS_PUBLIC_BUCKET ?? "", + storageKind: (process.env.DATASETS_STORAGE_KIND ?? "memory") as "local" | "memory", + localDir: process.env.DATASETS_LOCAL_DIR ?? "", + }, + guardianContractId: process.env.GUARDIAN_CONTRACT_ID ?? "", + canaryAddresses: (process.env.CANARY_ADDRESSES ?? "") + .split(",") + .map((a) => a.trim()) + .filter(Boolean), + datasets: { + enabled: (process.env.DATASETS_ENABLED ?? "false") === "true", + anonymize: (process.env.DATASETS_ANONYMIZE ?? "true") === "true", + salt: process.env.DATASETS_SALT ?? "", + saltRotationHours: parseInt(process.env.DATASETS_SALT_ROTATION_HOURS ?? "24", 10), + saltRetentionWindows: parseInt(process.env.DATASETS_SALT_RETENTION_WINDOWS ?? "2", 10), + publicBucket: process.env.DATASETS_PUBLIC_BUCKET ?? "vortex-public-datasets", + storageKind: (process.env.DATASETS_STORAGE ?? "local") as "local" | "memory", + localDir: process.env.DATASETS_LOCAL_DIR ?? ".datasets", + }, + secrets: { + provider: (process.env.SECRETS_PROVIDER ?? "env") as "env" | "aws-secrets-manager" | "vault-kv", + refreshIntervalMs: parseInt(process.env.SECRETS_REFRESH_INTERVAL_MS ?? "60000", 10), + extra: process.env.SECRETS_EXTRA ?? "", + ws: { + maxPayloadBytes: parseInt(process.env.WS_MAX_PAYLOAD_BYTES ?? "16384", 10), + maxConnectionsPerIp: parseInt(process.env.WS_MAX_CONNECTIONS_PER_IP ?? "20", 10), + trustProxyHops: parseInt(process.env.WS_TRUST_PROXY_HOPS ?? "0", 10), + rateLimitPerSec: Number(process.env.WS_RATE_LIMIT_PER_SEC ?? "10"), + rateLimitBurst: parseInt(process.env.WS_RATE_LIMIT_BURST ?? "20", 10), + rateLimitMaxViolations: parseInt(process.env.WS_RATE_LIMIT_MAX_VIOLATIONS ?? "5", 10), + outboundQueueMax: parseInt(process.env.WS_OUTBOUND_QUEUE_MAX ?? "1000", 10), + outboundBufferBytes: parseInt(process.env.WS_OUTBOUND_BUFFER_BYTES ?? "1048576", 10), + slowConsumerPolicy: (process.env.WS_SLOW_CONSUMER_POLICY ?? "drop_oldest") as AppConfig["ws"]["slowConsumerPolicy"], + drainTimeoutMs: parseInt(process.env.WS_DRAIN_TIMEOUT_MS ?? "25000", 10), + }, + authJwtSecret: process.env.AUTH_JWT_SECRET ?? "", + rateLimitLocalPruneMs: parseInt(process.env.RATE_LIMIT_LOCAL_PRUNE_MS ?? "60000", 10), + // Redis URL for the distributed rate limiter. Defaults to REDIS_URL so an + // existing multi-replica deployment keeps a global quota; set it explicitly + // to "" to force the bounded local limiter (single-replica / test). + rateLimitRedisUrl: process.env.RATE_LIMIT_REDIS_URL ?? process.env.REDIS_URL ?? "", + credentialRevocationPubsub: (process.env.CREDENTIAL_REVOCATION_PUBSUB ?? "memory") as "memory" | "redis", + sse: { + heartbeatMs: parseInt(process.env.SSE_HEARTBEAT_MS ?? "15000", 10), + maxBufferBytes: parseInt(process.env.SSE_MAX_BUFFER_BYTES ?? "1048576", 10), + }, + health: { + roles: (process.env.SERVICE_ROLES ?? "api,ws,worker") + .split(",") + .map((r) => r.trim()) + .filter(Boolean) as AppConfig["health"]["roles"], + checkIntervalMs: parseInt(process.env.HEALTH_CHECK_INTERVAL_MS ?? "5000", 10), + readyFailureThreshold: parseInt(process.env.HEALTH_READY_FAILURE_THRESHOLD ?? "3", 10), + readySuccessThreshold: parseInt(process.env.HEALTH_READY_SUCCESS_THRESHOLD ?? "2", 10), + eventLoopMaxLagMs: parseInt(process.env.HEALTH_EVENT_LOOP_MAX_LAG_MS ?? "1000", 10), + rpcHealthUrls: (process.env.SOROBAN_RPC_HEALTH_URLS || process.env.SOROBAN_RPC_URL || "https://soroban-testnet.stellar.org") + .split(",") + .map((u) => u.trim()) + .filter(Boolean), + }, +}); + +/** Parse `SHADOW_SAMPLE_RATE` into a probability, defaulting to full sampling. */ +function clampSampleRate(raw: string | undefined): number { + if (raw === undefined || raw.trim() === "") return 1; + const parsed = Number(raw); + if (!Number.isFinite(parsed)) return 1; + if (parsed < 0) return 0; + if (parsed > 1) return 1; + return parsed; +} + +/** + * Parse a positive integer env var, falling back to `fallback` for anything + * unparseable or non-positive. Keeps a typo from turning the bounded queue + * into an unbounded one. + */ +function clampPositiveInt(raw: string | undefined, fallback: number): number { + if (raw === undefined || raw.trim() === "") return fallback; + const parsed = Number.parseInt(raw, 10); + if (!Number.isFinite(parsed) || parsed < 1) return fallback; + return parsed; +} diff --git a/src/config/env.validation.ts b/src/config/env.validation.ts index e69de29b..c16266dd 100644 --- a/src/config/env.validation.ts +++ b/src/config/env.validation.ts @@ -0,0 +1,549 @@ +import * as Joi from "joi"; + +// Stellar secret seeds ("S..." strkeys) are 56-char base32: prefix + 32-byte +// payload + checksum. This rejects placeholders like "changeme" outright — +// it does not by itself prove the key is a *real, funded* signer. +const STELLAR_SECRET_KEY_PATTERN = /^S[A-Z2-7]{55}$/; + +// One message for both "absent" and "empty". Joi's .required() alone accepts an +// empty string, which for the kill-switch would be a silently disabled control +// plane — the exact condition this rule exists to prevent, so both cases must +// produce the same actionable error. +const KILLSWITCH_TOKEN_REQUIRED_MESSAGE = + "KILLSWITCH_OPERATOR_TOKEN must be a non-empty secret in production so the " + + "emergency pause control plane (/api/v1/ops/killswitch) is usable. Generate " + + "one with `openssl rand -hex 32`. See docs/runbooks/killswitch.md."; + +export const envValidationSchema = Joi.object({ + NODE_ENV: Joi.string().valid("development", "production", "test").default("development"), + PORT: Joi.number().port().default(4000), + + // Prisma requires DATABASE_URL in production; optional (with a default) in + // development/test so the app can boot without a live database for unit tests. + DATABASE_URL: Joi.string() + .uri({ scheme: ["postgresql", "postgres"] }) + .default("postgresql://vortex:vortex@localhost:5432/vortex?schema=public"), + + STELLAR_NETWORK: Joi.string().valid("testnet", "futurenet", "mainnet").default("testnet"), + SOROBAN_RPC_URL: Joi.string().uri().default("https://soroban-testnet.stellar.org"), + // Horizon base URL, used for account/balance reads (treasury, canary tooling). + HORIZON_URL: Joi.string().uri().default("https://horizon-testnet.stellar.org"), + SETTLEMENT_CONTRACT_ID: Joi.string().allow("").default(""), + SOLVER_REGISTRY_CONTRACT_ID: Joi.string().allow("").default(""), + STELLAR_SIGNER_SECRET_KEY: Joi.string().allow("").default(""), + + // Secret key for the backend's own Soroban signer (submits on-chain writes + // such as settlement and slashing calls). No default is provided anywhere + // in this schema — an unset value fails closed (empty string) rather than + // ever falling back to a placeholder that could be mistaken for a real key. + SOROBAN_SIGNING_KEY: Joi.string() + .pattern(STELLAR_SECRET_KEY_PATTERN) + .messages({ + "string.pattern.base": + 'SOROBAN_SIGNING_KEY must be a valid Stellar secret seed (starts with "S", 56 base32 characters). ' + + "Generate a throwaway testnet key for local dev — see README's Signing Key section — never commit a real one.", + }) + .when("NODE_ENV", { + is: "production", + then: Joi.required(), + otherwise: Joi.string().allow("").default(""), + }), + + ONCHAIN_INTENTS_ENABLED: Joi.boolean().default(false), + // Stellar public key of the treasury account (fee/slash/refund accumulator). + TREASURY_ADDRESS: Joi.string().allow("").default(""), + + ALLOW_LEGACY_STELLAR_SIGNATURES: Joi.boolean().default(false), + ETHEREUM_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + ETHEREUM_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + BASE_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + BASE_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + POLYGON_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + POLYGON_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + ARBITRUM_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + ARBITRUM_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + OPTIMISM_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + OPTIMISM_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + AVALANCHE_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + AVALANCHE_ESCROW_ADDRESS: Joi.string().pattern(/^$|^0x[a-fA-F0-9]{40}$/).default(""), + EVM_RPC_ALLOWLIST: Joi.string().allow("").default(""), + CORS_ORIGIN: Joi.string().default("*"), + WS_MAX_CONNECTIONS: Joi.number().integer().min(0).default(1000), + SOROBAN_FEE_PERCENTILE: Joi.string() + .valid( + "min", + "mode", + "p10", + "p20", + "p30", + "p40", + "p50", + "p60", + "p70", + "p80", + "p90", + "p95", + "p99", + "max", + ) + .default("p50"), + + WS_BACKPLANE: Joi.string().valid("memory", "redis").default("memory"), + REDIS_URL: Joi.string().uri({ scheme: ["redis", "rediss"] }).default("redis://localhost:6379"), + + // ── Persistence adapter selection ───────────────────────────────────────── + // Controls which repository adapter is used for intents and solvers. + // "memory" (default) keeps everything in-process — no database required. + // "prisma" writes to PostgreSQL via Prisma — requires DATABASE_URL to point + // to a live database. Intended for production / staging. + INTENTS_PERSISTENCE: Joi.string().valid("memory", "prisma").default("memory"), + SOLVERS_PERSISTENCE: Joi.string().valid("memory", "prisma").default("memory"), + + // ── Intent retention (in-memory store hygiene) ───────────────────────────── + // How long terminal intents are kept in the in-memory adapter, and how often + // the eviction sweep runs. Both are read by IntentsService. + INTENT_RETENTION_DAYS: Joi.number().integer().min(0).default(30), + INTENT_RETENTION_SWEEP_MS: Joi.number().integer().min(0).default(60000), + + // ── Reference solver bot (scripts/solver-bot.ts) ─────────────────────────── + // Read by the standalone bot process rather than by the server, but declared + // here so `npm run check:env-drift` sees one consistent variable set across + // env.validation.ts, configuration.ts and the .env*.example files. + SOLVER_SECRET: Joi.string().allow("").default(""), + SOLVER_ADDRESS: Joi.string().allow("").default(""), + SOLVER_CHAINS: Joi.string().allow("").default(""), + + // ── Observability ───────────────────────────────────────────────────────── + // Sentry DSN for error alerting. Omit (or leave blank) to disable Sentry. + SENTRY_DSN: Joi.string().uri().allow("").default(""), + + // Bearer token that guards GET /metrics (issue #298). + // When set, Prometheus scrape jobs must supply: + // Authorization: Bearer + // When empty (the default): + // - non-production: unauthenticated scraping allowed (local dev Prometheus) + // - production: endpoint returns 401 (fail-closed — set the token before deploying) + // Generate with: openssl rand -hex 32 + METRICS_TOKEN: Joi.string().allow("").default(""), + + // Winston log level. Defaults to "debug" in dev/test and "info" in production. + LOG_LEVEL: Joi.string() + .valid("error", "warn", "info", "http", "verbose", "debug", "silly") + .default( + // Joi.ref doesn't evaluate lazily here, so we rely on the logger's own + // resolveLogLevel() for the runtime default — this schema default acts + // as a documentation hint and config validation guard only. + "debug", + ), + + // Log shipping — off by default so local dev/CI remain stdout-only. When + // enabled, structured logs are also shipped to LOG_SHIPPING_HOST:PORT. + LOG_SHIPPING_ENABLED: Joi.boolean().default(false), + LOG_SHIPPING_HOST: Joi.string().when("LOG_SHIPPING_ENABLED", { + is: true, + then: Joi.required(), + otherwise: Joi.string().allow("").default(""), + }), + LOG_SHIPPING_PORT: Joi.number().port().when("LOG_SHIPPING_ENABLED", { + is: true, + then: Joi.required(), + otherwise: Joi.number().optional(), + }), + LOG_SHIPPING_PATH: Joi.string().default("/"), + LOG_SHIPPING_SSL: Joi.boolean().default(false), + LOG_SERVICE_NAME: Joi.string().default("vortex-backend"), + + // ── Pluggable signer backend (issue #400) ──────────────────────────────── + // SIGNER_BACKEND selects which signing implementation is used: + // "local" (default) — LocalKeypairSigner: key loaded from SOROBAN_SIGNING_KEY / file. + // Refused in production unless ALLOW_LOCAL_SIGNER_IN_PROD=true. + // "vault" — VaultTransitSigner: signs via HashiCorp Vault Transit (ed25519). + // Requires VAULT_ADDR + VAULT_TOKEN. Key never enters RAM. + SIGNER_BACKEND: Joi.string().valid("local", "vault").default("local"), + + // Required when SIGNER_BACKEND=vault. + VAULT_ADDR: Joi.string().uri({ scheme: ["http", "https"] }).when("SIGNER_BACKEND", { + is: "vault", + then: Joi.required(), + otherwise: Joi.string().allow("").default(""), + }), + VAULT_TOKEN: Joi.string().when("SIGNER_BACKEND", { + is: "vault", + then: Joi.required(), + otherwise: Joi.string().allow("").default(""), + }), + // Name of the Vault Transit key (default: "vortex-signer"). + VAULT_TRANSIT_KEY_NAME: Joi.string().default("vortex-signer"), + + // Escape hatch: allow LocalKeypairSigner in production. + // Must be explicitly set to "true" — any other value is treated as false. + // A startup warning is emitted when this is enabled in production. + ALLOW_LOCAL_SIGNER_IN_PROD: Joi.boolean().default(false), + + // ── Resource-exhaustion limits (issue #476) ─────────────────────────────── + // These values are consumed by src/config/limits.config.ts at startup and + // override the compile-time defaults when set. All have safe defaults so + // the service can boot without them. + + /** Max JSON nesting depth before the body is rejected (default 10). */ + JSON_MAX_DEPTH: Joi.number().integer().min(1).max(100).default(10), + + /** Max WS chain-filter values per subscribe message (default 20). */ + WS_MAX_FILTER_CHAINS: Joi.number().integer().min(1).max(100).default(20), + + /** Max active subscriptions per WS connection (default 10). */ + WS_MAX_SUBSCRIPTIONS: Joi.number().integer().min(1).max(100).default(10), + + /** Default Postgres statement_timeout in ms for standard route queries (default 5000). */ + DB_QUERY_TIMEOUT_MS: Joi.number().integer().min(100).max(60000).default(5000), + + /** Postgres statement_timeout in ms for batch-lookup queries (default 10000). */ + DB_BATCH_QUERY_TIMEOUT_MS: Joi.number().integer().min(100).max(60000).default(10000), + + /** Postgres statement_timeout in ms for stats/aggregate queries (default 15000). */ + DB_STATS_QUERY_TIMEOUT_MS: Joi.number().integer().min(100).max(60000).default(15000), + + // ── Emergency kill-switch (issue #477) ───────────────────────────────────── + // Shared secret for the operator control plane. Empty (the default) leaves + // /api/v1/ops/killswitch disabled — fail closed, never open. + // + // The kill-switch is the only way to stop writes at runtime, so a production + // deploy without a token ships a protocol that cannot be paused. Requiring it + // in production fails validation rather than silently running with the + // control plane disabled. + KILLSWITCH_OPERATOR_TOKEN: Joi.string() + .when("NODE_ENV", { + is: Joi.valid("production"), + then: Joi.string() + .required() + .invalid("") + .messages({ + "any.required": KILLSWITCH_TOKEN_REQUIRED_MESSAGE, + "string.empty": KILLSWITCH_TOKEN_REQUIRED_MESSAGE, + "any.invalid": KILLSWITCH_TOKEN_REQUIRED_MESSAGE, + }), + otherwise: Joi.string().allow("").default(""), + }), + + /** + * Redis URL for cross-replica pause propagation. Empty means "polling only", + * which still meets the 5 s budget. Defaults to reusing REDIS_URL when + * WS_BACKPLANE=redis, so existing deployments propagate without new config. + */ + KILLSWITCH_REDIS_URL: Joi.string().allow("").optional(), + + /** + * DB change-probe interval (ms) that backstops Redis pub/sub. Capped at 5000 + * so the worst-case propagation delay cannot exceed the requirement, however + * misconfigured. + */ + KILLSWITCH_POLL_MS: Joi.number().integer().min(100).max(5000).default(2000), + + // Same adapter-selection convention as the other repositories. + KILLSWITCH_PERSISTENCE: Joi.string().valid("memory", "prisma").default("memory"), + + // ── On-chain write safety flag (issue #35 / issue #260) ────────────────── + // When true, every on-chain-write code path (invokeContract, slashSolver) + // builds and simulates the transaction, logs what it *would* submit, and + // returns without broadcasting — safe by construction. + // + // Default behaviour: + // - Outside production: defaults to true (simulate-only, fail closed + // toward safety — no real funds moved without an explicit opt-out). + // - In production: *required* to be explicitly set. Omitting it in a + // production deploy fails validation so the operator must consciously + // decide between dry-run and live mode before traffic reaches + // on-chain write paths. This matches the fail-closed pattern used + // for SOROBAN_SIGNING_KEY. + // + // This is the env default for the `onchain-dry-run` runtime feature flag + // (issue #495); the flag can override it without a restart, and turning + // dry-run off in production through the flag requires two approvals. + // Set ONCHAIN_DRY_RUN=false only after completing the dry-run soak + // described in docs/runbooks/onchain-cutover.md. + ONCHAIN_DRY_RUN: Joi.boolean() + .when("NODE_ENV", { + is: "production", + then: Joi.required().messages({ + "any.required": + "ONCHAIN_DRY_RUN must be explicitly set in production. " + + "Set to true to remain in simulate-only mode, or false to enable live on-chain writes. " + + "See docs/runbooks/onchain-cutover.md for the staged rollout procedure.", + }), + otherwise: Joi.boolean().default(true), + }), + + // ── Shadow-mode divergence monitor (issue #401) ─────────────────────────── + // Runs read-only on-chain simulations of every intent state transition in + // parallel with the authoritative off-chain path and reports where the two + // disagree. Never submits a transaction; see src/soroban/shadow.service.ts. + // + // Off by default: a sampled simulation is a real RPC call with a real + // rate-limit footprint, so it is an explicit per-environment opt-in. + SHADOW_MODE_ENABLED: Joi.boolean().default(false), + + // Fraction of transitions to simulate, as a probability in [0, 1]. + // 1 (the default) compares every transition; 0 disables sampling entirely + // while leaving the monitor "enabled" — useful for a canary that only wants + // the queue/metric plumbing live. + SHADOW_SAMPLE_RATE: Joi.number().min(0).max(1).default(1), + + // Hard cap on queued observations. Beyond this, observations are dropped and + // counted (`vortex_shadow_dropped_total`) rather than queued, so a slow or + // unreachable RPC degrades the monitor instead of the service. + SHADOW_QUEUE_MAX: Joi.number().integer().min(1).default(256), + + // How many queued observations the background drain simulates concurrently. + SHADOW_CONCURRENCY: Joi.number().integer().min(1).max(32).default(4), + + // Public key used as the transaction source for shadow simulations. A Stellar + // public key (strkey G...). It is never signed, never submitted and never + // charged a fee — it only has to be a valid address for the envelope. + // Optional: when empty the monitor reports `contract_unconfigured` rather + // than silently recording zero divergence. + SHADOW_SOURCE_ACCOUNT: Joi.string().allow("").default(""), + + // ── Governance parameters contract ──────────────────────────────────────── + // When set, ProtocolParamsService reads current + scheduled protocol + // parameters (fee bps, fill windows, deadlines, exposure ratio, slash + // amount) from this Soroban contract address. Leave blank to use code + // and env defaults. + PARAMS_CONTRACT_ID: Joi.string().allow("").default(""), + + // How often (ms) to poll the parameters contract. 30 s is the default; + // lower values increase RPC load; raise in production if rate-limited. + PARAMS_POLL_INTERVAL_MS: Joi.number().integer().min(5_000).default(30_000), + + // ── Leader election (issue #493) ────────────────────────────────────────── + // Controls whether Postgres advisory-lock based leader election is enabled + // for singleton workers (sweeper, event-ingestion). + // + // Set LEADER_ELECTION_ENABLED=false in single-instance dev deployments or + // when no database is available. When disabled, every worker considers + // itself leader unconditionally — the pre-election behaviour. + // + // IMPORTANT: Do NOT route the leader election connection through PgBouncer + // in transaction-pooling mode. Advisory locks are session-scoped; they are + // released when the connection is returned to the pool. Use a direct + // connection or PgBouncer in session mode. + LEADER_ELECTION_ENABLED: Joi.boolean().default(false), + + // Heartbeat interval in milliseconds — how often non-leaders attempt to + // acquire the lock and leaders renew it. Lower values reduce failover time + // but increase DB load. Default 5 s gives ≤ 15 s failover. + LEADER_ELECTION_HEARTBEAT_MS: Joi.number().integer().min(1000).max(60000).default(5000), + + // ── Background jobs (issue #494) ────────────────────────────────────────── + // PROCESS_ROLE: "api" serves HTTP/WS only, "worker" runs queue workers, + // "all" does both (single-process dev default). Producers work in any role. + PROCESS_ROLE: Joi.string().valid("api", "worker", "all").default("all"), + // JOBS_DRIVER: "memory" is single-process and non-durable (dev/test); + // "bullmq" uses REDIS_URL and is required for multi-instance deploys. + JOBS_DRIVER: Joi.string().valid("memory", "bullmq").default("memory"), + JOBS_SHUTDOWN_TIMEOUT_MS: Joi.number().integer().min(0).default(25000), + + // ── Runtime feature flags (issue #495) ──────────────────────────────────── + FLAGS_PUBSUB: Joi.string().valid("memory", "redis").default("memory"), + FLAGS_REFRESH_MS: Joi.number().integer().min(1000).default(30000), + // Comma-separated "key=true|false" pins that win over DB state (break-glass). + FLAG_OVERRIDES: Joi.string() + .allow("") + .pattern(/^([a-z0-9-]+=(true|false))(,[a-z0-9-]+=(true|false))*$/) + .default(""), + + // ── Admin RBAC ──────────────────────────────────────────────────────────── + // Comma-separated "id:role:secret" entries; role is "admin" or "superadmin". + // Empty disables every admin endpoint (401). + ADMIN_API_KEYS: Joi.string() + .allow("") + .pattern(/^([A-Za-z0-9_.-]+:(admin|superadmin):[^,:]{16,})(,[A-Za-z0-9_.-]+:(admin|superadmin):[^,:]{16,})*$/) + .default(""), + + // ── Public anonymised datasets ──────────────────────────────────────────── + DATASETS_ENABLED: Joi.boolean().default(false), + DATASETS_ANONYMIZE: Joi.boolean().default(true), + DATASETS_SALT: Joi.string().allow("").default(""), + DATASETS_SALT_ROTATION_HOURS: Joi.number().integer().min(1).max(720).default(24), + DATASETS_SALT_RETENTION_WINDOWS: Joi.number().integer().min(0).max(30).default(2), + DATASETS_PUBLIC_BUCKET: Joi.string().allow("").default(""), + DATASETS_STORAGE_KIND: Joi.string().valid("local", "memory").default("memory"), + DATASETS_LOCAL_DIR: Joi.string().allow("").default(""), + + // ── Guardian emergency ingestion (issue #507) ───────────────────────────── + GUARDIAN_CONTRACT_ID: Joi.string().allow("").default(""), + + // ── Synthetic canary (issue #496) ───────────────────────────────────────── + // Comma-separated canary user/solver addresses, excluded from public stats + // and leaderboards. + CANARY_ADDRESSES: Joi.string().allow("").default(""), + + // ── Public anonymised datasets (docs/rfcs/0001) ─────────────────────────── + DATASETS_ENABLED: Joi.boolean().default(false), + DATASETS_ANONYMIZE: Joi.boolean().default(true), + // Required only when datasets are enabled AND anonymisation is on — an + // empty/weak salt would collapse pseudonymisation to a fixed, reversible + // transform. It stays optional (default "") otherwise so existing dev/test + // configs are unaffected. + DATASETS_SALT: Joi.string() + .when("DATASETS_ENABLED", { + is: true, + then: Joi.string().when("DATASETS_ANONYMIZE", { + is: true, + then: Joi.string() + .min(32) + .required() + .messages({ + "any.required": + "DATASETS_SALT must be set when DATASETS_ENABLED=true and DATASETS_ANONYMIZE=true. " + + "Generate a strong random secret (e.g. `openssl rand -hex 32`).", + "string.min": "DATASETS_SALT must be at least 32 characters.", + }), + otherwise: Joi.string().allow("").default(""), + }), + otherwise: Joi.string().allow("").default(""), + }), + DATASETS_SALT_ROTATION_HOURS: Joi.number().integer().min(1).default(24), + DATASETS_SALT_RETENTION_WINDOWS: Joi.number().integer().min(0).default(2), + DATASETS_PUBLIC_BUCKET: Joi.string().default("vortex-public-datasets"), + DATASETS_STORAGE: Joi.string().valid("local", "memory").default("local"), + DATASETS_LOCAL_DIR: Joi.string().default(".datasets"), + + // ── Secrets Manager (issue #465) ──────────────────────────────────────────── + SECRETS_PROVIDER: Joi.string().valid("env", "aws-secrets-manager", "vault-kv").default("env"), + SECRETS_REFRESH_INTERVAL_MS: Joi.number().integer().min(5000).default(60000), + SECRETS_EXTRA: Joi.string().allow("").default(""), + + // AWS Secrets Manager + AWS_SECRETS_MANAGER_PREFIX: Joi.string().allow("").default(""), + AWS_SECRETS_MANAGER_POLL_INTERVAL_MS: Joi.number().integer().min(5000).default(60000), + + // Vault KV + VAULT_KV_MOUNT: Joi.string().default("secret"), + VAULT_KV_PREFIX: Joi.string().default("vortex/"), + VAULT_KV_POLL_INTERVAL_MS: Joi.number().integer().min(5000).default(60000), + + // Extra secret env vars referenced by the default SecretConfig + JWT_SIGNING_KEY: Joi.string().allow("").default(""), + WEBHOOK_SECRET: Joi.string().allow("").default(""), + CHANNEL_KEY: Joi.string().allow("").default(""), + // ── Egress / SSRF Protection (issue #468) ───────────────────────────────── + // Controls the centralized HttpEgressService used for all outbound HTTP requests + // (RPC, Horizon, oracles, webhooks) to prevent SSRF attacks. + EGRESS_TIMEOUT_MS: Joi.number().integer().min(1000).max(60000).default(10000), + EGRESS_MAX_REDIRECTS: Joi.number().integer().min(0).max(5).default(3), + EGRESS_MAX_BODY_SIZE_BYTES: Joi.number().integer().min(1024).default(10485760), // 10MB + SOROBAN_RPC_ALLOWLIST: Joi.string().allow("").default(""), + WEBHOOK_ALLOWLIST: Joi.string().allow("").default(""), + ORACLE_ALLOWLIST: Joi.string().allow("").default(""), + + // ── WS gateway hardening (issue #455) ───────────────────────────────────── + WS_MAX_PAYLOAD_BYTES: Joi.number().integer().min(1024).default(16384), + WS_MAX_CONNECTIONS_PER_IP: Joi.number().integer().min(0).default(20), + // Hops of trusted reverse proxies in front of the service. 0 ignores + // X-Forwarded-For entirely so clients cannot spoof their IP. + WS_TRUST_PROXY_HOPS: Joi.number().integer().min(0).default(0), + WS_RATE_LIMIT_PER_SEC: Joi.number().positive().default(10), + WS_RATE_LIMIT_BURST: Joi.number().integer().min(1).default(20), + WS_RATE_LIMIT_MAX_VIOLATIONS: Joi.number().integer().min(1).default(5), + WS_OUTBOUND_QUEUE_MAX: Joi.number().integer().min(1).default(1000), + WS_OUTBOUND_BUFFER_BYTES: Joi.number().integer().min(1024).default(1048576), + WS_SLOW_CONSUMER_POLICY: Joi.string().valid("drop_oldest", "disconnect").default("drop_oldest"), + // HS256 secret shared with the SEP-10 auth endpoint (#442). Empty disables + // JWT auth; signature auth keeps working. + AUTH_JWT_SECRET: Joi.string().allow("").min(32).default(""), + + // ── Distributed rate limiting (issue #441) ───────────────────────────────── + // How often the local rate-limiter fallback prunes expired window entries. + // Only relevant during a Redis outage; keeps the fallback map bounded. + RATE_LIMIT_LOCAL_PRUNE_MS: Joi.number().integer().min(1000).max(60000).default(60000), + + // Redis URL for the distributed rate limiter (#441). Leave empty to force the + // bounded local limiter (the limit is still enforced, per process). + RATE_LIMIT_REDIS_URL: Joi.string().allow("").optional(), + + // ── Scoped solver credentials (issue #443) ───────────────────────────────── + // Cross-replica transport for credential revocation invalidation. + CREDENTIAL_REVOCATION_PUBSUB: Joi.string().valid("memory", "redis").default("memory"), + + // ── SSE intent feed (issue #433) ─────────────────────────────────────────── + // Heartbeat comment interval and per-client backpressure limit for the + // Server-Sent Events intent stream. + SSE_HEARTBEAT_MS: Joi.number().integer().min(1000).max(60000).default(15000), + SSE_MAX_BUFFER_BYTES: Joi.number().integer().min(1024).default(1048576), + + // ── Health probes (issue #492) ──────────────────────────────────────────── + // Comma-separated roles this process serves: api, ws, worker. + SERVICE_ROLES: Joi.string() + .pattern(/^(api|ws|worker)(,(api|ws|worker))*$/) + .default("api,ws,worker"), + HEALTH_CHECK_INTERVAL_MS: Joi.number().integer().min(500).default(5000), + HEALTH_READY_FAILURE_THRESHOLD: Joi.number().integer().min(1).default(3), + HEALTH_READY_SUCCESS_THRESHOLD: Joi.number().integer().min(1).default(2), + HEALTH_EVENT_LOOP_MAX_LAG_MS: Joi.number().integer().min(50).default(1000), + // Comma-separated Soroban RPC URLs for the RPC-quorum readiness check. + // Defaults to SOROBAN_RPC_URL. + SOROBAN_RPC_HEALTH_URLS: Joi.string().allow("").default(""), + + // ── Soroban RPC pool & archival (#509 #510) ─────────────────────────────── + // Comma-separated Soroban RPC endpoints; falls back to SOROBAN_RPC_URL. + SOROBAN_RPC_URLS: Joi.string().allow("").default(""), + // Optional archival RPC used for ledger-entry history reads. + ARCHIVAL_RPC_URL: Joi.string().uri({ scheme: ["http", "https"] }).allow("").default(""), + // Signing channel pool sizing for concurrent on-chain submissions. + CHANNEL_POOL_SIZE: Joi.number().integer().min(1).max(64).default(8), + // Comma-separated secret seeds, one per signing channel. + CHANNEL_SECRET_KEYS: Joi.string().allow("").default(""), + // Fee ceiling for on-chain submissions, in stroops. + SOROBAN_MAX_FEE_STROOPS: Joi.number().integer().min(0).default(1000000), + // Seconds after which a stale reconcile cursor triggers a rescan. + RECONCILE_STALE_SECONDS: Joi.number().integer().min(1).default(300), + + // ── Postgres intents store (#404 #405) ──────────────────────────────────── + // Backend for persisted intents: in-memory or Postgres-backed. + INTENTS_STORE: Joi.string().valid("memory", "postgres").default("memory"), + // Interval between background verification passes over the stored intents. + INTENTS_VERIFY_INTERVAL_MS: Joi.number().integer().min(1000).default(60000), + + // ── Oracle fair-value validation (#548) ─────────────────────────────────── + // Max quote slippage versus oracle fair value before a quote is rejected. + MAX_USER_SLIPPAGE_BPS: Joi.number().integer().min(0).default(100), + // Max premium over fair value a solver may quote before rejection. + MAX_PREMIUM_BPS: Joi.number().integer().min(0).default(50), + // Oracle staleness tolerance before prices are considered unusable. + ORACLE_MAX_STALENESS_MS: Joi.number().integer().min(1000).default(60000), + // Aggregate USD value for which a stale feed fails open instead of closed. + ORACLE_FAIL_OPEN_MAX_USD: Joi.number().min(0).default(100), + + // ── Transactional outbox (#396 #397) ────────────────────────────────────── + OUTBOX_RELAY_ENABLED: Joi.boolean().default(true), + OUTBOX_RELAY_INTERVAL_MS: Joi.number().integer().min(100).default(2000), + OUTBOX_RELAY_BATCH_SIZE: Joi.number().integer().min(1).default(10), + OUTBOX_MAX_ATTEMPTS: Joi.number().integer().min(1).default(8), + OUTBOX_LEASE_SECONDS: Joi.number().integer().min(1).default(120), + + // ── Durable slashing saga (#396 #397) ───────────────────────────────────── + SLASH_CHALLENGE_WINDOW_SECONDS: Joi.number().integer().min(0).default(600), + SLASH_CLOCK_SKEW_TOLERANCE_SECONDS: Joi.number().integer().min(0).default(30), + SLASH_MAX_SUBMIT_ATTEMPTS: Joi.number().integer().min(1).default(5), + + // ── Protocol fees (#551) ────────────────────────────────────────────────── + // JSON array of versioned fee rules applied when quoting swaps. + FEE_RULES_JSON: Joi.string().default("[]"), + // JSON array of referral-configured fee overrides. + FEE_REFERRALS_JSON: Joi.string().default("[]"), + + // ── EVM deposit verification (#402-#405) ────────────────────────────────── + EVM_DEPOSIT_VERIFICATION_ENABLED: Joi.boolean().default(false), + // JSON object mapping chain id to RPC URL (e.g. {"1":"https://..."}). + EVM_RPC_URLS: Joi.string().default("{}"), + // Comma-separated escrow contract addresses accepted for deposits. + EVM_ESCROW_ADDRESSES: Joi.string().allow("").default(""), + EVM_TRANSFER_FEE_TOLERANCE_BPS: Joi.number().integer().min(0).default(0), + EVM_LOG_LOOKBACK_BLOCKS: Joi.number().integer().min(0).default(10000), + + // ── Maintenance sweeps & WS shutdown (#547 #511) ────────────────────────── + // Interval for the background safety sweep over stuck intents. + SAFETY_SWEEP_INTERVAL_MS: Joi.number().integer().min(1000).default(300000), + // Grace period for draining open WebSocket connections on shutdown. + WS_DRAIN_TIMEOUT_MS: Joi.number().integer().min(0).default(25000), +}); diff --git a/src/governance/governance.module.ts b/src/governance/governance.module.ts index e69de29b..0ebaac87 100644 --- a/src/governance/governance.module.ts +++ b/src/governance/governance.module.ts @@ -0,0 +1,32 @@ +import { forwardRef, Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; +import { ProtocolParamsService } from "./params.service"; +import { ParamsController } from "./params.controller"; +import { SorobanModule } from "../soroban/soroban.module"; +import { GuardianController } from "./guardian.controller"; +import { GuardianService } from "./guardian.service"; + +// GovernanceModule → SorobanModule → IntentsModule → GovernanceModule forms a +// cycle; the SorobanModule edge must be deferred so the import resolves after +// SorobanModule has finished loading. + +/** + * Governance module — exposes protocol parameters sourced from the on-chain + * governance / parameters contract, and ingests guardian emergency actions + * (issue #507). + * + * Exports `ProtocolParamsService` so other modules (e.g. `IntentsModule`) can + * inject it to snapshot parameters at intent-creation time. + * + * `SorobanModule` is imported through `forwardRef`: SorobanModule <-> + * IntentsModule is an existing CommonJS cycle, and IntentsModule imports this + * module, so a bare import would resolve to `undefined` while SorobanModule is + * still mid-initialization. + */ +@Module({ + imports: [forwardRef(() => SorobanModule)], + controllers: [ParamsController, GuardianController], + providers: [ProtocolParamsService, GuardianService], + exports: [ProtocolParamsService, GuardianService], +}) +export class GovernanceModule {} diff --git a/src/intents/dto/cancel-intent.dto.ts b/src/intents/dto/cancel-intent.dto.ts index e69de29b..e5c15038 100644 --- a/src/intents/dto/cancel-intent.dto.ts +++ b/src/intents/dto/cancel-intent.dto.ts @@ -0,0 +1,29 @@ +import { IsInt, IsOptional, IsString, Matches, Max, MaxLength, Min, MinLength } from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; + +export class CancelIntentDto { + @ApiProperty({ description: "Stellar or EVM address of the intent's original creator (must match)", maxLength: 56 }) + @IsString() + @MinLength(10) + @MaxLength(56) + user!: string; + + @ApiPropertyOptional({ description: "Single-use signing nonce", minLength: 16, maxLength: 128 }) + @IsOptional() + @IsString() + @Matches(/^[A-Za-z0-9_-]{16,128}$/) + nonce?: string; + + @ApiPropertyOptional({ description: "Unix timestamp when the signature expires" }) + @IsOptional() + @IsInt() + @Min(1) + @Max(4102444800) + expiresAt?: number; + + @ApiProperty({ description: "Base64 Ed25519 or EIP-712 signature proving control of user", maxLength: 4096 }) + @IsString() + @Matches(/^(?:[A-Za-z0-9+/]{4,}={0,2}|0x(?:[0-9a-fA-F]{2})+)$/) + @MaxLength(4096) + signature!: string; +} diff --git a/src/intents/dto/create-intent.dto.ts b/src/intents/dto/create-intent.dto.ts index e69de29b..3b4e5926 100644 --- a/src/intents/dto/create-intent.dto.ts +++ b/src/intents/dto/create-intent.dto.ts @@ -0,0 +1,179 @@ +import { + IsIn, + IsInt, + IsOptional, + IsString, + Matches, + Max, + MaxLength, + Min, + MinLength, + ValidateNested, + registerDecorator, + ValidationArguments, + ValidationOptions, +} from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { SUPPORTED_CHAINS, SupportedChain } from "../intents.types"; +import { IsValidAddress } from "../../common/validators/is-valid-address.validator"; +import { IsValidDeadline } from "../../common/validators/deadline.validator"; + +export class DutchAuctionDto { + @ApiProperty({ description: "Starting destination amount in base units" }) + @IsString() + @Matches(/^\d+$/) + startDstAmount!: string; + + @ApiProperty({ description: "Unix timestamp when the price decay starts" }) + @IsInt() + @Min(1) + @Max(4102444800) + decayStart!: number; + + @ApiProperty({ description: "Unix timestamp when the price reaches minDstAmount" }) + @IsInt() + @Min(1) + @Max(4102444800) + decayEnd!: number; + + @ApiPropertyOptional({ description: "Solver address exclusively eligible until exclusivityEnd" }) + @IsOptional() + @IsString() + @Matches(/^(G[A-Z2-7]{55}|0x[a-fA-F0-9]{40})$/) + @MaxLength(56) + exclusiveSolver?: string; + + @ApiPropertyOptional({ description: "Unix timestamp when solver exclusivity ends" }) + @IsOptional() + @IsInt() + @Min(1) + @Max(4102444800) + exclusivityEnd?: number; +} + +function IsNotSelfSwap(validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: "isNotSelfSwap", + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: unknown, args: ValidationArguments) { + const obj = args.object as Record; + const srcChain = obj.srcChain; + const srcTokenAddress = obj.srcTokenAddress; + const dstTokenContract = value; + + return !(srcChain === "stellar" && srcTokenAddress === dstTokenContract); + }, + defaultMessage() { + return "Self-swaps are not allowed: a Stellar asset cannot be swapped against itself on the same chain"; + }, + }, + }); + }; +} + +export class CreateIntentDto { + @ApiProperty({ description: "Stellar or EVM address of the user creating the intent", maxLength: 56 }) + @IsString() + @MinLength(10) + @MaxLength(56) + user!: string; + + @ApiProperty({ enum: SUPPORTED_CHAINS, description: "Source chain the funds are coming from" }) + @IsIn(SUPPORTED_CHAINS) + srcChain!: SupportedChain; + + @ApiProperty({ description: "Source token contract/address on srcChain" }) + @IsValidAddress() + srcTokenAddress!: string; + + @ApiProperty({ description: "Source token symbol, e.g. USDC", maxLength: 16 }) + @IsString() + @MaxLength(16) + srcTokenSymbol!: string; + + @ApiProperty({ minimum: 0, maximum: 18, description: "Source token decimals" }) + @IsInt() + @Min(0) + @Max(18) + srcTokenDecimals!: number; + + @ApiProperty({ description: "Source amount as a non-negative integer string (base units)" }) + @IsString() + @Matches(/^\d+$/) + srcAmount!: string; + + @ApiProperty({ description: "Destination Stellar token contract", maxLength: 56 }) + @IsString() + @Matches(/^[A-Z0-9]{56}$/) + @MaxLength(56) + @IsNotSelfSwap() + dstTokenContract!: string; + + @ApiProperty({ description: "Destination token symbol, e.g. USDC", maxLength: 16 }) + @IsString() + @MaxLength(16) + dstTokenSymbol!: string; + + @ApiProperty({ minimum: 0, maximum: 18, description: "Destination token decimals" }) + @IsInt() + @Min(0) + @Max(18) + dstTokenDecimals!: number; + + @ApiProperty({ description: "Minimum acceptable destination amount as an integer string" }) + @IsString() + @Matches(/^\d+$/) + minDstAmount!: string; + + @ApiPropertyOptional({ type: DutchAuctionDto, description: "Optional Dutch-auction allocation terms" }) + @IsOptional() + @ValidateNested() + @Type(() => DutchAuctionDto) + auction?: DutchAuctionDto; + + @ApiPropertyOptional({ description: "Unix timestamp deadline; defaults to now + 1800s; must be between now+60s and now+24h" }) + @IsOptional() + @IsInt() + @IsValidDeadline() + deadline?: number; + + @ApiPropertyOptional({ description: "EIP-712 signature for EVM-originated intent creation", maxLength: 4096 }) + @IsOptional() + @IsString() + @Matches(/^0x(?:[0-9a-fA-F]{2})+$/) + @MaxLength(4096) + signature?: string; + + @ApiPropertyOptional({ description: "Unique nonce included in the EIP-712 signature", maxLength: 128 }) + @IsOptional() + @IsString() + @Matches(/^[A-Za-z0-9_x-]{16,128}$/) + nonce?: string; + + @ApiPropertyOptional({ description: "Unix timestamp when the EIP-712 signature expires" }) + @IsOptional() + @IsInt() + @Min(1) + @Max(4102444800) + expiresAt?: number; + + @ApiPropertyOptional({ description: "Idempotency key for deduplicating duplicate requests" }) + @IsOptional() + @IsString() + idempotencyKey?: string; + + @ApiPropertyOptional({ + description: + "EVM transaction hash of the escrow deposit (issue #403). Optional: when supplied, verification " + + "reads that receipt directly instead of scanning recent blocks for the Deposited log.", + example: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", + }) + @IsOptional() + @Matches(/^0x[0-9a-fA-F]{64}$/, { message: "srcTxHash must be a 0x-prefixed 32-byte transaction hash" }) + srcTxHash?: string; +} diff --git a/src/intents/dto/fill-intent.dto.ts b/src/intents/dto/fill-intent.dto.ts index e69de29b..337f7a6f 100644 --- a/src/intents/dto/fill-intent.dto.ts +++ b/src/intents/dto/fill-intent.dto.ts @@ -0,0 +1,49 @@ +import { IsInt, IsOptional, IsString, Matches, Max, MaxLength, Min, MinLength } from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsString, Matches, MaxLength, MinLength } from "class-validator"; +import { ApiProperty } from "@nestjs/swagger"; + +const ED25519_SIGNATURE_MAX_LENGTH = 88; + +export class FillIntentDto { + @ApiProperty({ description: "Solver address filling the intent (must match the accepting solver)", maxLength: 56 }) + @IsString() + @MinLength(5) + @MaxLength(56) + solver!: string; + + @ApiProperty({ description: "Amount filled, as a non-negative integer string" }) + @IsString() + @Matches(/^\d+$/) + fillAmount!: string; + + @ApiProperty({ description: "Stellar fill transaction hash, independently checked against Horizon", maxLength: 128 }) + @IsString() + @MinLength(64) + @Matches(/^[0-9a-fA-F]{64}$/) + txHash!: string; + + @ApiPropertyOptional({ description: "Single-use signing nonce", minLength: 16, maxLength: 128 }) + @IsOptional() + @IsString() + @Matches(/^[A-Za-z0-9_-]{16,128}$/) + nonce?: string; + + @ApiPropertyOptional({ description: "Unix timestamp when the signature expires" }) + @IsOptional() + @IsInt() + @Min(1) + @Max(4102444800) + expiresAt?: number; + + @ApiProperty({ + description: + 'Base64-encoded Ed25519 signature of the message "fill::" ' + + "produced by the solver's private key", + maxLength: ED25519_SIGNATURE_MAX_LENGTH, + }) + @IsString() + @MinLength(10) + @MaxLength(ED25519_SIGNATURE_MAX_LENGTH) + signature!: string; +} diff --git a/src/intents/dto/list-intents.dto.ts b/src/intents/dto/list-intents.dto.ts index e69de29b..61fdaaaa 100644 --- a/src/intents/dto/list-intents.dto.ts +++ b/src/intents/dto/list-intents.dto.ts @@ -0,0 +1,130 @@ +import { IsIn, IsInt, IsOptional, IsString, Max, Min, Matches } from "class-validator"; +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { + INTENT_STATES, + IntentState, + SUPPORTED_CHAINS, + SupportedChain, +} from "../intents.types"; +import { LIST_MAX_LIMIT } from "../../config/limits.config"; + +/** + * Sort dimensions supported by `GET /intents` (issue #440). + * + * Each dimension accepts an optional `:asc` / `:desc` direction suffix. + * The default is `created:desc` — identical to the pre-existing behaviour, + * so omitting `sort` changes nothing. + */ +const SORT_PATTERN = /^(created|deadline|usd)(:(asc|desc))?$/; + +export class ListIntentsDto { + @ApiPropertyOptional({ + description: "Filter by intent state", + enum: INTENT_STATES, + }) + @IsOptional() + @IsIn(INTENT_STATES) + state?: IntentState; + + @ApiPropertyOptional({ description: "Filter by user address" }) + @IsOptional() + @IsString() + user?: string; + + @ApiPropertyOptional({ + description: "Filter by source chain", + enum: SUPPORTED_CHAINS, + }) + @IsOptional() + @IsIn(SUPPORTED_CHAINS) + chain?: SupportedChain; + + // ── Advanced filters (issue #440) ──────────────────────────────────────── + + @ApiPropertyOptional({ + description: "Minimum USD value at creation (inclusive)", + minimum: 0, + }) + @IsOptional() + @IsInt() + @Min(0) + minAmountUsd?: number; + + @ApiPropertyOptional({ + description: "Maximum USD value at creation (inclusive)", + minimum: 0, + }) + @IsOptional() + @IsInt() + @Min(0) + maxAmountUsd?: number; + + @ApiPropertyOptional({ + description: "Minimum creation time (unix epoch seconds, inclusive)", + }) + @IsOptional() + @IsInt() + @Min(0) + createdFrom?: number; + + @ApiPropertyOptional({ + description: "Maximum creation time (unix epoch seconds, inclusive)", + }) + @IsOptional() + @IsInt() + @Min(0) + createdTo?: number; + + @ApiPropertyOptional({ + description: "Filter by source token symbol (case-insensitive)", + }) + @IsOptional() + @IsString() + srcToken?: string; + + @ApiPropertyOptional({ + description: "Filter by destination token symbol (case-insensitive)", + }) + @IsOptional() + @IsString() + dstToken?: string; + + @ApiPropertyOptional({ + description: "Filter by solver address that accepted/filled the intent", + }) + @IsOptional() + @IsString() + solver?: string; + + @ApiPropertyOptional({ + description: "Sort dimension and direction: created|deadline|usd, optionally with :asc or :desc (default created:desc)", + }) + @IsOptional() + @Matches(SORT_PATTERN, { + message: "sort must be one of created, deadline, or usd, optionally followed by :asc or :desc", + }) + sort?: string; + + @ApiPropertyOptional({ + minimum: 1, + maximum: LIST_MAX_LIMIT, + default: 20, + description: `Number of results per page (max ${LIST_MAX_LIMIT})`, + }) + @IsOptional() + @IsInt() + @Min(1) + @Max(LIST_MAX_LIMIT) + limit?: number; + + @ApiPropertyOptional({ description: "Cursor for the next page of intents" }) + @IsOptional() + @IsString() + cursor?: string; + + @ApiPropertyOptional({ minimum: 0, default: 0, description: "Number of results to skip" }) + @IsOptional() + @IsInt() + @Min(0) + offset?: number; +} diff --git a/src/intents/intents-sweeper.service.ts b/src/intents/intents-sweeper.service.ts index e69de29b..0c9d6777 100644 --- a/src/intents/intents-sweeper.service.ts +++ b/src/intents/intents-sweeper.service.ts @@ -0,0 +1,252 @@ +import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; +import { IntentsService } from "./intents.service"; +import { IntentsGateway } from "./intents.gateway"; +import { SolversService } from "../solvers/solvers.service"; +import { SlashingPipelineService } from "./slashing-pipeline.service"; +import { logger } from "../common/logger"; +import { MetricsService } from "../metrics/metrics.service"; +import { KillSwitchService } from "../killswitch/killswitch.service"; +import { Intent } from "./intents.types"; +import { + CHAIN_FILL_WINDOW_DEFAULTS, + DEFAULT_FILL_WINDOW_SECONDS, +} from "../config/configuration"; +import { LeaderElectionService, Singleton } from "../common/leader-election"; + +const SWEEP_INTERVAL_MS = 30_000; + +/** Outcome of a single sweep cycle — returned so a manual trigger can log it. */ +export interface SweepResult { + expiredCount: number; + slashedCount: number; + /** Intents whose fill window was pushed out because a pause blocked fills. */ + extendedDeadlines: number; + durationMs: number; +} + +@Singleton("sweeper") +@Injectable() +export class IntentsSweeperService implements OnModuleInit, OnModuleDestroy { + private readonly logger = new Logger(IntentsSweeperService.name); + private interval?: NodeJS.Timeout; + + constructor( + private readonly intentsService: IntentsService, + private readonly intentsGateway: IntentsGateway, + private readonly solversService: SolversService, + private readonly slashingPipeline: SlashingPipelineService, + private readonly metricsService: MetricsService, + private readonly killSwitch: KillSwitchService, + private readonly leaderElection: LeaderElectionService, + ) {} + + onModuleInit() { + this.leaderElection.registerWorker("sweeper", (isLeader, _token) => { + if (isLeader) { + this.logger.log("[sweeper] became leader — starting interval"); + this.startInterval(); + } else { + this.logger.log("[sweeper] lost leadership — stopping interval"); + this.stopInterval(); + } + }); + } + + onModuleDestroy() { + this.stopInterval(); + } + + private startInterval(): void { + if (this.interval) return; // already running + this.interval = setInterval(() => { + this.sweep().catch((err) => { + logger.error(`[sweeper] sweep failed: ${err instanceof Error ? err.message : err}`); + }); + }, SWEEP_INTERVAL_MS); + } + + private stopInterval(): void { + if (this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + } + + async sweep(): Promise { + const startMs = Date.now(); + const now = Math.floor(startMs / 1000); + let expiredCount = 0; + let slashedCount = 0; + let extendedDeadlines = 0; + + for (const intent of await this.intentsService.getByState("open")) { + if (intent.deadline <= now) { + // Atomic guard: a concurrent user cancel() or solver accept() may have + // already transitioned this intent out of "open" — skip it if so. + const expired = await this.intentsService.expireIfOpen(intent.intentId); + if (!expired) continue; + // Audit trail (issue #62): system-driven expiration. + this.intentsService.appendAuditEntry( + intent.intentId, + "expired", + "system", + "deadline passed", + { deadline: intent.deadline, sweepedAt: now }, + ); + expiredCount++; + await this.intentsGateway.broadcast({ type: "intent_expired", intentId: intent.intentId }); + } + } + + const durationMs = Date.now() - startMs; + + // Record sweep metrics into the Prometheus-backed MetricsService (issue #259). + // This replaces the retired MetricsRegistry from src/common/metrics.ts. + this.metricsService.recordSweep(expiredCount, durationMs); + + this.logger.debug(`sweep complete: expired=${expiredCount} duration=${durationMs}ms`); + + if (expiredCount > 0) { + this.logger.log(`[sweeper] Expired ${expiredCount} intent(s) in ${durationMs}ms`); + } + + const missedFills = (await this.intentsService.getByState("accepted")).filter( + (intent) => intent.deadline <= now, + ); + + for (const intent of missedFills) { + // Issue #477 — an emergency pause must not punish solvers for a pause we + // imposed. When the fill path is paused for this intent's scope, extend + // its window instead of slashing; the intent becomes fillable again on + // resume. Evaluated per intent because a pause may be scoped to a single + // chain or token. + const deadline = this.pausedFillDeadline(intent, now); + if (deadline !== null) { + const extended = await this.intentsService.extendDeadlineIfAccepted( + intent.intentId, + deadline, + ); + if (extended) { + extendedDeadlines++; + this.logger.warn( + `[sweeper] intent ${intent.intentId} fill is paused by a kill-switch — ` + + `slashing suppressed and deadline extended to ${deadline}`, + ); + } + continue; + } + + const slashed = await this.slashMissedFill(intent.intentId, intent.solver, intent.deadline, now); + if (slashed) slashedCount++; + } + + return { + expiredCount, + slashedCount, + extendedDeadlines, + durationMs: Date.now() - startMs, + }; + } + + /** + * Returns the new deadline to grant when fills are paused for this intent, or + * null when slashing should proceed. + * + * Grants a full fill window from now rather than a fixed bump, so an intent + * caught by a long pause still gets a fair window once the pause lifts. + */ + private pausedFillDeadline(intent: Intent, now: number): number | null { + const decision = this.killSwitch.evaluateTarget({ + chain: intent.srcChain, + token: intent.srcToken?.address, + operation: "fill", + }); + if (!decision.paused) return null; + + const window = CHAIN_FILL_WINDOW_DEFAULTS[intent.srcChain] ?? DEFAULT_FILL_WINDOW_SECONDS; + return now + window; + } + + /** + * Issue #269 — safe, auditable manual sweep trigger (operator break-glass). + * + * Runs exactly one sweep cycle on demand and logs the invocation loudly — + * source, timestamp, and result — so a manual trigger is unmistakable in an + * incident timeline. Wired to `SIGUSR2` in `main.ts`; there is deliberately + * no HTTP surface, so it is not reachable by any API client. + */ + async triggerManualSweep(source: string): Promise { + const invokedAt = new Date().toISOString(); + this.logger.warn( + `[sweeper] MANUAL SWEEP TRIGGERED (source=${source}, invokedAt=${invokedAt}) — running one sweep cycle`, + ); + + try { + const result = await this.sweep(); + this.logger.warn( + `[sweeper] MANUAL SWEEP COMPLETE (source=${source}, invokedAt=${invokedAt}): ` + + `expired=${result.expiredCount} slashed=${result.slashedCount} duration=${result.durationMs}ms`, + ); + return result; + } catch (err) { + this.logger.error( + `[sweeper] MANUAL SWEEP FAILED (source=${source}, invokedAt=${invokedAt}): ` + + `${err instanceof Error ? err.message : err}`, + ); + throw err; + } + } + + /** + * Detection half of the slashing saga (issue #397). Marks the intent + * `slashed`, applies the optimistic local penalty, and hands off to + * SlashingPipelineService, which holds the slash in a challenge window, + * re-verifies, and only then broadcasts — nothing is sent on-chain here. + */ + private async slashMissedFill( + intentId: string, + solver: string | undefined, + fillDeadline: number, + now: number, + ): Promise { + const reason = "accepted intent not filled before deadline"; + + // Atomic guard: a concurrent solver fill() may have already transitioned + // this intent out of "accepted" — skip slashing if so (fill wins). + const slashed = await this.intentsService.slashIfAccepted(intentId, { + slashedAt: now, + slashReason: reason, + }); + if (!slashed) return false; + this.intentsService.appendAuditEntry(intentId, "slashed", "system", reason, { + solver, + slashedAt: now, + }); + await this.intentsGateway.broadcast({ type: "intent_slashed", intentId, solver, reason, pending: true }); + + if (!solver) { + // Shouldn't happen in practice — an "accepted" intent always has a + // solver — but don't let a bad record throw the whole sweep cycle. + logger.error(`[sweeper] intent ${intentId} was accepted with no solver on record`); + return true; + } + + // Optimistic local penalty; the saga compensates via rollbackPenalty if + // the slash is cancelled. + await this.solversService.recordFailedFill(solver, intentId); + const slashRecord = await this.solversService.recordSlash(solver, intentId, reason, now); + + const pending = await this.slashingPipeline.detect({ + intentId, + solverAddress: solver, + reason, + fillDeadline, + detectedAt: now, + }); + this.logger.log( + `[sweeper] slash detected solver=${solver} intent=${intentId} state=${pending.state} ` + + `challengeEndsAt=${pending.challengeEndsAt.toISOString()} slashId=${slashRecord?.slashId ?? "unknown"}`, + ); + return true; + } +} diff --git a/src/intents/intents.controller.ts b/src/intents/intents.controller.ts index e69de29b..36e66f67 100644 --- a/src/intents/intents.controller.ts +++ b/src/intents/intents.controller.ts @@ -0,0 +1,760 @@ +import { + BadRequestException, + Body, + ConflictException, + Controller, + ForbiddenException, + Get, + GoneException, + NotFoundException, + Param, + Post, + Query, + ServiceUnavailableException, + UseGuards, +} from "@nestjs/common"; +import { + ApiTags, + ApiOkResponse, + ApiNotFoundResponse, + ApiConflictResponse, + ApiForbiddenResponse, + ApiGoneResponse, + ApiBadRequestResponse, + ApiTooManyRequestsResponse, + ApiOperation, + ApiServiceUnavailableResponse, +} from "@nestjs/swagger"; +import { Throttle } from "@nestjs/throttler"; +import { IntentsService } from "./intents.service"; +import { IntentsGateway } from "./intents.gateway"; +import { SolversService } from "../solvers/solvers.service"; +import { TokensService } from "../tokens/tokens.service"; +import { RoutingService } from "../routing/routing.service"; +import { MAX_OPEN_INTENTS_PER_USER } from "./intents.service"; +import { CreateIntentDto } from "./dto/create-intent.dto"; +import { CHAIN_DEADLINE_DEFAULTS, DEFAULT_DEADLINE_SECONDS } from "../config/configuration"; +import { AcceptIntentDto } from "./dto/accept-intent.dto"; +import { FillIntentDto } from "./dto/fill-intent.dto"; +import { CancelIntentDto } from "./dto/cancel-intent.dto"; +import { QuoteRequestDto } from "./dto/quote-request.dto"; +import { QuoteResponseDto } from "./dto/quote-response.dto"; +import { ListIntentsDto } from "./dto/list-intents.dto"; +import { BatchLookupDto } from "./dto/batch-lookup.dto"; +import { UserThrottlerGuard } from "./user-throttler.guard"; +import { + verifyStellarSignature, + buildAcceptMessage, + buildCancelMessage, + buildFillMessage, +} from "../common/stellar-signature"; +import { + applyVarianceScale, + calculateProtocolFee, + parseBaseUnits, + toDecimalNumber, + varianceScaleFromPerfScore, +} from "../common/amount"; +import { Intent, SupportedChain } from "./intents.types"; +import { + assertNotPaused, + KillSwitchGate, + KillSwitchGuard, +} from "../killswitch/killswitch.guard"; +import { KillSwitchService } from "../killswitch/killswitch.service"; +import { KillSwitchOperation } from "../killswitch/killswitch.types"; +import { ConfigService } from "@nestjs/config"; +import { AppConfig } from "../config/configuration"; +import { isCanaryIntent } from "../common/canary"; +import { SolverBondService } from "../soroban/solver-bond.service"; +import { ProtocolParamsService } from "../governance/params.service"; +import { baseUnitsToUsdMicros, intentExposureUsdMicros } from "./intent-exposure"; + +@ApiTags("intents") +@Controller("api/v1/intents") +export class IntentsController { + constructor( + private readonly intentsService: IntentsService, + private readonly solversService: SolversService, + private readonly intentsGateway: IntentsGateway, + private readonly tokensService: TokensService, + private readonly routingService: RoutingService, + private readonly killSwitch: KillSwitchService, + config: ConfigService, + private readonly solverBondService: SolverBondService, + private readonly protocolParamsService: ProtocolParamsService, + ) { + this.canary = new Set(config.get("canaryAddresses", { infer: true }) ?? []); + } + + /** Canary addresses (issue #496). */ + private readonly canary: ReadonlySet; + + /** + * Re-assert the kill-switch hierarchy against a *loaded* intent. + * + * `KillSwitchGuard` runs before the handler and can only read the route path + * and body. For `:id` routes that is not enough to evaluate a chain- or + * token-scoped pause, so `accept` and `fill` call this once the record is in + * hand. The global-scope and snapshot-readiness checks are still done by the + * guard, so this is strictly additional coverage, not a replacement. + */ + private assertIntentNotPaused(intent: Intent, operation: KillSwitchOperation): void { + assertNotPaused( + this.killSwitch, + { + chain: intent.srcChain, + // Prefer the contract address: symbols are not unique within a chain, + // so a symbol-scoped pause would over-match and an address-scoped one + // would under-match. Operators pause by address. + token: intent.srcToken?.address ?? null, + operation, + }, + { retryAfterSeconds: 30 }, + ); + } + + @Get() + @ApiBadRequestResponse({ description: "Invalid limit or offset" }) + async list(@Query() dto: ListIntentsDto) { + let intents = await this.intentsService.getAll(); + + if (dto.state) intents = intents.filter((i) => i.state === dto.state); + if (dto.user) intents = intents.filter((i) => i.user.toLowerCase() === dto.user!.toLowerCase()); + if (dto.chain) intents = intents.filter((i) => i.srcChain === dto.chain); + + const limit = Math.min(dto.limit ?? 20, 100); + const offset = dto.offset ?? 0; + + if ((dto.limit ?? 20) > 100) { + throw new BadRequestException("Limit exceeds maximum allowed value of 100"); + } + + const page = intents.slice(offset, offset + limit); + return { intents: page, total: intents.length, limit, offset }; + } + + @Get("open") + async listOpen(@Query() dto: ListIntentsDto) { + const open = await this.intentsService.getByState("open"); + const limit = Math.min(dto.limit ?? 20, 100); + const offset = dto.offset ?? 0; + + if ((dto.limit ?? 20) > 100) { + throw new BadRequestException("Limit exceeds maximum allowed value of 100"); + } + + const page = open.slice(offset, offset + limit); + return { intents: page, total: open.length, count: open.length, limit, offset }; + } + + @Get("user/:address") + async listByUser(@Param("address") address: string, @Query() dto: ListIntentsDto) { + const intents = await this.intentsService.getByUser(address); + const limit = Math.min(dto.limit ?? 20, 100); + const offset = dto.offset ?? 0; + + if ((dto.limit ?? 20) > 100) { + throw new BadRequestException("Limit exceeds maximum allowed value of 100"); + } + + const page = intents.slice(offset, offset + limit); + return { intents: page, total: intents.length, count: intents.length, limit, offset }; + } + + @Get(":id") + @ApiNotFoundResponse({ description: "Intent not found" }) + async getOne(@Param("id") id: string) { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + return intent; + } + + /** + * GET /api/v1/intents/:id/audit + * + * Returns the full state-transition history for an intent, oldest-first. + * Issue #217 — backs the in-memory audit trail with a persistent DB table + * (intent_audit_log) so the log survives restarts and is independently + * queryable (see DATABASE_INDEXES.md section 3 and the runbooks that depend + * on this trail: docs/runbooks/onchain-cutover.md, RUNBOOK_BACKUP_RESTORE.md). + */ + @Get(":id/audit") + @ApiOperation({ + summary: "Get audit trail for an intent", + description: + "Returns the full state-transition history for an intent ordered oldest-first. " + + "Each entry records the state the intent moved into, who triggered it, and why.", + }) + @ApiOkResponse({ + description: "Audit trail for the intent", + schema: { + type: "object", + properties: { + intentId: { type: "string" }, + entries: { + type: "array", + items: { + type: "object", + properties: { + timestamp: { type: "string", format: "date-time" }, + toState: { type: "string" }, + actor: { type: "string" }, + reason: { type: "string" }, + metadata: { type: "object", nullable: true }, + }, + }, + }, + }, + }, + }) + @ApiNotFoundResponse({ description: "Intent not found" }) + async getAudit(@Param("id") id: string, @Query() dto: ListIntentsDto) { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + + const limit = Math.min(dto.limit ?? 20, 100); + const offset = dto.offset ?? 0; + if ((dto.limit ?? 20) > 100) { + throw new BadRequestException("Limit exceeds maximum allowed value of 100"); + } + + const allEntries = this.intentsService.getAuditLog(id); + const entries = this.intentsService.getAuditLog(id, limit, offset); + const total = allEntries.length; + return { intentId: id, entries, total, limit, offset }; + } + + /** + * GET /api/v1/intents/:id/quote + * + * Returns the persisted best quote for an intent (the quotedDstAmount stored + * on the intent after a POST /quote call with intentId). + */ + @Get(":id/quote") + @ApiOkResponse({ description: "Persisted quote for the intent" }) + @ApiNotFoundResponse({ description: "Intent not found or no quote persisted" }) + async getPersistedQuote(@Param("id") id: string) { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + if (!intent.quotedDstAmount) throw new NotFoundException("No quote persisted for this intent"); + return { intentId: id, quotedDstAmount: intent.quotedDstAmount }; + } + + /** + * Issue #44 — global IP throttle already applied via AppModule guard. + * Issue #45 — additionally throttle per dto.user: 10 creates / 60 s. + */ + @Post() + @UseGuards(UserThrottlerGuard, KillSwitchGuard) + @KillSwitchGate({ operation: "create" }) + @ApiTooManyRequestsResponse({ + description: + "Rate limit exceeded — max 10 intent creations per user per 60 s (or 100 req/min per IP globally)", + }) + @ApiBadRequestResponse({ description: "Invalid request body" }) + @ApiConflictResponse({ + description: `Open-intent cap reached — a single user may not hold more than ${MAX_OPEN_INTENTS_PER_USER} open/accepted intents simultaneously`, + }) + async create(@Body() dto: CreateIntentDto) { + const now = Math.floor(Date.now() / 1000); + + // #219: use typed resolveToken instead of ad-hoc duck-typed any casts. + // #276: reject unrecognised tokens outright instead of silently creating an + // intent whose priceUSD defaults to undefined. + // #473: enforce the per-user open-intent cap as a fast-path rejection. + // The atomic guarantee lives in the persistence layer (conditional write); + // this pre-check keeps the common over-cap case cheap without adding a + // round trip on the happy path. + const openCount = await this.intentsService.countOpenByUser(dto.user); + if (openCount >= MAX_OPEN_INTENTS_PER_USER) { + throw new ConflictException( + `Open-intent cap reached — max ${MAX_OPEN_INTENTS_PER_USER} open/accepted intents per user`, + ); + } + const srcToken = await this.tokensService.resolveSrcTokenOrThrow( + dto.srcChain as SupportedChain, + dto.srcTokenAddress, + ); + const dstToken = await this.tokensService.resolveDstTokenOrThrow(dto.dstTokenContract); + + const intent = await this.intentsService.create( + { + user: dto.user, + srcChain: dto.srcChain, + srcToken: { + address: dto.srcTokenAddress, + symbol: dto.srcTokenSymbol, + name: dto.srcTokenSymbol, + decimals: dto.srcTokenDecimals, + chain: dto.srcChain, + priceUSD: srcToken?.priceUSD, + }, + srcAmount: dto.srcAmount, + dstToken: { + contract: dto.dstTokenContract, + symbol: dto.dstTokenSymbol, + decimals: dto.dstTokenDecimals, + priceUSD: dstToken?.priceUSD, + }, + minDstAmount: dto.minDstAmount, + deadline: dto.deadline ?? now + (CHAIN_DEADLINE_DEFAULTS[dto.srcChain] ?? DEFAULT_DEADLINE_SECONDS), + }, + dto.idempotencyKey, + ); + this.intentsGateway.broadcast({ type: "intent_created", intent }); + return intent; + } + + /** + * POST /api/v1/intents/batch + * + * Issue #275 — bounded batch status lookup. Lets a solver bot (or a frontend + * showing a full history) reconcile a known set of intent IDs against current + * server state in one call instead of N `GET /:id` requests. + * + * `POST` (not `GET`) because the ID list can exceed a comfortable query-string + * length. Subject to the same global rate limits as every other endpoint — + * no dedicated tier. Read-only: batch accept/fill/cancel is explicitly out of + * scope. + */ + @Post("batch") + @ApiOperation({ + summary: "Batch-fetch current intent records by ID", + description: + "Returns the current record for each supplied intent ID. IDs with no " + + "matching record are omitted (not individually 404'd). Capped at 100 IDs.", + }) + @ApiOkResponse({ description: "Records for the found intent IDs, plus a count" }) + @ApiBadRequestResponse({ + description: "intentIds missing, not an array of strings, or exceeds 100 entries", + }) + async batchLookup(@Body() dto: BatchLookupDto) { + const intents = await this.intentsService.getMany(dto.intentIds); + return { intents, count: intents.length }; + } + + @Post(":id/accept") + @UseGuards(KillSwitchGuard) + @KillSwitchGate({ operation: "accept" }) + @ApiNotFoundResponse({ description: "Intent not found" }) + @ApiConflictResponse({ description: "Intent is not in open state" }) + @ApiGoneResponse({ description: "Intent has expired" }) + @ApiForbiddenResponse({ description: "Solver not registered or inactive" }) + @ApiServiceUnavailableResponse({ description: "Solver bond or a fresh USD price could not be verified" }) + async accept(@Param("id") id: string, @Body() dto: AcceptIntentDto) { + // Fast-path snapshot only — guards below are advisory. The atomic + // decision is the conditional `acceptIfOpen` write (state=open AND + // deadline > now in SQL), so a concurrent cancel/expiry always wins. + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + + // The guard above can only see the path parameter, so it could not know + // which chain/token this intent belongs to. Re-assert now that the record + // is loaded, otherwise a chain- or token-scoped pause would not stop + // accepts. Deliberately placed after the 404 so an unknown id still 404s. + this.assertIntentNotPaused(intent, "accept"); + + const now = Math.floor(Date.now() / 1000); + if (intent.deadline <= now) { + // Atomic expiry attempt: never blindly overwrite — an `accepted` + // intent must slash, never expire (issue #473). + await this.intentsService.expireIfOpen(id); + throw new GoneException("Intent has expired"); + } + + // Verify the solver controls the claimed address before it can accept. + verifyStellarSignature(dto.solver, buildAcceptMessage(id, dto.solver), dto.signature); + + if (this.solversService.isSuspended(dto.solver)) { + throw new ForbiddenException("Solver is suspended by an active guardian action"); + } + // Canary intents pair only with canary solvers (issue #496) so synthetic + // traffic never affects real solvers' stats or real users' fills. + if (isCanaryIntent(intent, this.canary) !== this.canary.has(dto.solver)) { + throw new ForbiddenException("Canary intents may only be accepted by canary solvers, and vice versa"); + } + + const chainBond = await this.solverBondService.getBond(dto.solver); + if (!chainBond.isActive || chainBond.bondAmount <= 0n) { + throw new ForbiddenException({ + code: "INSUFFICIENT_BOND", + error: "Solver has no active on-chain bond", + message: "Solver has no active on-chain bond", + }); + } + + const xlmPrice = await this.tokensService.getUsdPrice("XLM"); + const bondUsdMicros = baseUnitsToUsdMicros(chainBond.bondAmount, 7, xlmPrice); + const candidateExposureUsdMicros = intentExposureUsdMicros(intent, now); + const ratio = this.protocolParamsService.getCurrent().maxExposureRatio; + if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) { + throw new ServiceUnavailableException({ + code: "INVALID_EXPOSURE_RATIO", + error: "The configured maximum exposure ratio is invalid", + }); + } + const ratioScale = 1_000_000_000n; + const ratioFixed = BigInt(Math.floor(ratio * Number(ratioScale))); + const maxExposureUsdMicros = (bondUsdMicros * ratioFixed) / ratioScale; + + const capacity = await this.intentsService.acceptIfOpenWithinExposure( + id, + dto.solver, + candidateExposureUsdMicros, + maxExposureUsdMicros, + now, + ); + if (capacity.exposureExceeded) { + throw new ForbiddenException({ + code: "INSUFFICIENT_BOND", + error: "Accepted exposure would exceed the solver's bond limit", + message: "Accepted exposure would exceed the solver's bond limit", + }); + } + const updated = capacity.intent; + if (!updated) { + const current = await this.intentsService.get(id); + if (!current) throw new NotFoundException("Intent not found"); + if ((current.deadline ?? 0) <= Math.floor(Date.now() / 1000)) { + throw new GoneException("Intent has expired"); + } + throw new ConflictException(`Intent is ${current?.state ?? "unknown"}, cannot accept`); + } + + this.intentsService.appendAuditEntry(id, "accepted", dto.solver, "solver accepted", { + deadline: updated.deadline, + }); + this.intentsGateway.broadcast({ + type: "intent_accepted", + intentId: id, + solver: dto.solver, + }); + return updated; + } + + @Post(":id/fill") + @UseGuards(KillSwitchGuard) + @KillSwitchGate({ operation: "fill" }) + @ApiNotFoundResponse({ description: "Intent not found" }) + @ApiServiceUnavailableResponse({ + description: "An emergency kill-switch is active for this intent's scope (503 + Retry-After)", + }) + @ApiConflictResponse({ description: "Intent is not in accepted state" }) + @ApiForbiddenResponse({ description: "Wrong solver for this intent" }) + @ApiGoneResponse({ description: "Fill window has expired" }) + @ApiBadRequestResponse({ description: "Fill amount below minimum" }) + async fill(@Param("id") id: string, @Body() dto: FillIntentDto) { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + + // Same reason as in `accept`: the route guard cannot resolve the intent's + // chain/token from `:id`, so re-assert against the loaded record. + this.assertIntentNotPaused(intent, "fill"); + + const now = Math.floor(Date.now() / 1000); + if (intent.deadline <= now) { + throw new GoneException("Fill window has expired"); + } + + // Verify the solver controls the claimed address + verifyStellarSignature(dto.solver, buildFillMessage(id, dto.solver), dto.signature); + + const fillAmount = parseBaseUnits(dto.fillAmount); + let minAmount: bigint; + try { + minAmount = BigInt(intent.minDstAmount); + } catch { + throw new BadRequestException({ + error: "Data integrity error: intent minDstAmount is not a valid integer", + intentId: id, + minDstAmount: intent.minDstAmount, + }); + } + if (fillAmount < minAmount) { + throw new BadRequestException({ + error: "Fill amount below minimum", + fillAmount: dto.fillAmount, + minDstAmount: intent.minDstAmount, + }); + } + + const feeAmount = (BigInt(dto.fillAmount) * 5n) / 10000n; + + const updated = await this.intentsService.fillIfAccepted(id, dto.solver, { + filledAt: now, + fillAmount: dto.fillAmount, + feeAmount: feeAmount.toString(), + txHash: dto.txHash, + }); + if (!updated) { + const current = await this.intentsService.get(id); + if (current?.solver !== dto.solver) { + throw new ForbiddenException("Wrong solver for this intent"); + } + throw new ConflictException(`Intent is ${current?.state ?? "unknown"}, cannot fill`); + } + + await this.solversService.recordSuccessfulFill(dto.solver); + + this.intentsService.appendAuditEntry(id, "filled", dto.solver, "solver filled", { + fillAmount: dto.fillAmount, + txHash: dto.txHash, + }); + this.intentsGateway.broadcast({ + type: "intent_filled", + intentId: id, + solver: dto.solver, + fillAmount: dto.fillAmount, + }); + return updated; + } + + @Post(":id/cancel") + @ApiNotFoundResponse({ description: "Intent not found" }) + @ApiForbiddenResponse({ description: "Unauthorized" }) + @ApiConflictResponse({ description: "Intent is not in open state" }) + async cancel(@Param("id") id: string, @Body() dto: CancelIntentDto) { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + if (intent.user.toLowerCase() !== dto.user.toLowerCase()) { + throw new ForbiddenException("Unauthorized"); + } + if (intent.state !== "open") { + throw new ConflictException(`Cannot cancel intent in state: ${intent.state}`); + } + + // Verify the user controls the claimed address + verifyStellarSignature(dto.user, buildCancelMessage(id), dto.signature); + + const updated = await this.intentsService.cancelIfOpen(id); + if (!updated) { + const current = await this.intentsService.get(id); + throw new ConflictException(`Cannot cancel intent in state: ${current?.state ?? "unknown"}`); + } + + // Audit trail (issue #217 / #62): record who cancelled and when. + this.intentsService.appendAuditEntry(id, "cancelled", dto.user, "user cancelled"); + + this.intentsGateway.broadcast({ type: "intent_cancelled", intentId: id }); + return updated; + } + + /** + * Issue #44 — document 429 on quote too, since it's under the global guard. + * Issue #220 — routes are now computed via RoutingService and attached to each quote. + */ + @Post("quote") + @Throttle({ default: { limit: 20, ttl: 60_000 } }) + @ApiTooManyRequestsResponse({ + description: "Rate limit exceeded — max 20 quote requests per 60 s per IP", + }) + @ApiOkResponse({ type: QuoteResponseDto }) + async quote(@Body() dto: QuoteRequestDto): Promise { + const solvers = (await this.solversService.getAll()).filter((s) => s.isActive); + + // #219: use typed resolveSrcToken / resolveDstToken — no more any casts. + // #276: a quote may be requested by symbol alone (no contract/address), but + // when a token identifier IS supplied it must resolve — otherwise the quote + // engine would silently substitute a fake $1 price. + const srcToken = dto.srcTokenAddress + ? await this.tokensService.resolveSrcTokenOrThrow( + dto.srcChain as SupportedChain, + dto.srcTokenAddress, + ) + : undefined; + const dstToken = dto.dstTokenContract + ? await this.tokensService.resolveDstTokenOrThrow(dto.dstTokenContract) + : undefined; + + const srcAmountBigInt = parseBaseUnits(dto.srcAmount); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const dstPriceUSD: number = (dstToken as any)?.priceUSD ?? 1; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const srcPriceUSD: number = (srcToken as any)?.priceUSD ?? dstPriceUSD; + + const quotes = solvers + .map((solver) => { + // Issue #118: weight variance by solver performance history. + const totalFills = solver.fillsCompleted + solver.fillsFailed; + const successRate = totalFills > 0 ? solver.fillsCompleted / totalFills : 0.5; + const fillCountScore = Math.min(solver.fillsCompleted / 100, 1); + const perfScore = successRate * 0.7 + fillCountScore * 0.3; + const varianceScaled = varianceScaleFromPerfScore(perfScore); + const dstAmount = applyVarianceScale(srcAmountBigInt, varianceScaled); + const fee = calculateProtocolFee(dstAmount); // 0.05% + + // Issue #126: compute USD fee total and price impact. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const feeUnits = toDecimalNumber(fee, (dstToken as any)?.decimals ?? 7); + const totalFeesUSD = feeUnits * dstPriceUSD; + const srcUnits = toDecimalNumber(srcAmountBigInt, srcToken?.decimals ?? 7); + const dstUnits = toDecimalNumber(dstAmount, dstToken?.decimals ?? 7); + const priceImpact = + srcPriceUSD > 0 && dstPriceUSD > 0 + ? Math.max(0, 1 - (dstUnits * dstPriceUSD) / (srcUnits * srcPriceUSD)) + : 0; + + // #220: attach a computed route to each solver quote. + // Build minimal TokenInfo objects for routing (uses resolved data when available). + const srcTokenInfo = { + address: dto.srcTokenAddress ?? "", + symbol: dto.srcTokenSymbol, + name: srcToken?.name ?? dto.srcTokenSymbol, + decimals: srcToken?.decimals ?? 18, + chain: (dto.srcChain as SupportedChain) ?? "ethereum", + priceUSD: srcToken?.priceUSD, + }; + const dstTokenInfo = { + address: dstToken?.contract ?? dto.dstTokenContract ?? "", + symbol: dto.dstTokenSymbol, + name: dstToken?.name ?? dto.dstTokenSymbol, + decimals: dstToken?.decimals ?? 7, + chain: "stellar" as SupportedChain, + priceUSD: dstToken?.priceUSD, + }; + + // Try a direct route; fall back to a two-hop via USDC intermediate when + // a direct solver path is not viable (different base tokens). + const route = this.routingService.buildRoute(srcTokenInfo, dstTokenInfo, solver.address, { + totalFeesUSD, + priceImpact, + estimatedFillTime: solver.avgFillTime + Math.floor(Math.random() * 30), + }); + + return { + solver: solver.address, + solverName: solver.name, + dstAmount: dstAmount.toString(), + fee: fee.toString(), + fillTime: solver.avgFillTime + Math.floor(Math.random() * 30), + expiresAt: Math.floor(Date.now() / 1000) + 60, + totalFeesUSD, + priceImpact, + route, + }; + }) + // nosemgrep: no-number-money -- sort comparator on bounded quote diffs only; amounts stay strings elsewhere. + .sort((a, b) => Number(BigInt(b.dstAmount) - BigInt(a.dstAmount))); + + if (dto.intentId && quotes.length > 0) { + await this.intentsService.update(dto.intentId, { quotedDstAmount: quotes[0].dstAmount }); + } + + const best = quotes[0] ?? null; + return { + quotes, + bestQuote: best, + srcChain: dto.srcChain, + srcTokenSymbol: dto.srcTokenSymbol, + srcAmount: dto.srcAmount, + dstTokenSymbol: dto.dstTokenSymbol, + estimatedFillTime: best?.fillTime ?? 0, + totalFeesUSD: best?.totalFeesUSD ?? 0, + priceImpact: best?.priceImpact ?? 0, + }; + } + + /** + * POST /api/v1/intents/:id/requote + * + * Convenience endpoint for re-quoting an already-created intent without + * resupplying srcChain/srcToken/srcAmount/dstToken — they're read straight + * off the stored Intent record. Only valid while the intent is "open". + */ + @Post(":id/requote") + @Throttle({ default: { limit: 20, ttl: 60_000 } }) + @ApiOperation({ summary: "Re-quote an existing open intent using its stored fields" }) + @ApiTooManyRequestsResponse({ + description: "Rate limit exceeded — max 20 quote requests per 60 s per IP", + }) + @ApiOkResponse({ type: QuoteResponseDto }) + @ApiNotFoundResponse({ description: "Intent not found" }) + @ApiConflictResponse({ description: "Intent is not in the open state" }) + async requote(@Param("id") id: string): Promise { + const intent = await this.intentsService.get(id); + if (!intent) throw new NotFoundException("Intent not found"); + if (intent.state !== "open") { + throw new ConflictException( + `Cannot requote intent in state "${intent.state}"; only open intents can be requoted`, + ); + } + + const solvers = (await this.solversService.getAll()).filter((s) => s.isActive); + const srcToken = intent.srcToken; + const dstToken = intent.dstToken; + const srcAmountBigInt = BigInt(intent.srcAmount); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const dstPriceUSD: number = (dstToken as any)?.priceUSD ?? 1; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const srcPriceUSD: number = (srcToken as any)?.priceUSD ?? dstPriceUSD; + + const quotes = solvers + .map((solver) => { + const totalFills = solver.fillsCompleted + solver.fillsFailed; + const successRate = totalFills > 0 ? solver.fillsCompleted / totalFills : 0.5; + const fillCountScore = Math.min(solver.fillsCompleted / 100, 1); + const perfScore = successRate * 0.7 + fillCountScore * 0.3; + const variancePct = (1 - perfScore) * 0.008; + const varianceScaled = Math.round(1000 * (1 - variancePct)); + const dstAmount = (srcAmountBigInt * BigInt(varianceScaled)) / BigInt(1000); + const fee = (dstAmount * BigInt(5)) / BigInt(10000); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const feeUnits = Number(fee) / Math.pow(10, (dstToken as any)?.decimals ?? 7); + const totalFeesUSD = feeUnits * dstPriceUSD; + const srcUnits = Number(srcAmountBigInt) / Math.pow(10, srcToken?.decimals ?? 7); + const dstUnits = Number(dstAmount) / Math.pow(10, dstToken?.decimals ?? 7); + const priceImpact = + srcPriceUSD > 0 && dstPriceUSD > 0 + ? Math.max(0, 1 - (dstUnits * dstPriceUSD) / (srcUnits * srcPriceUSD)) + : 0; + + const dstTokenInfo = { + address: dstToken?.contract ?? "", + symbol: dstToken?.symbol ?? "", + name: dstToken?.symbol ?? "", + decimals: dstToken?.decimals ?? 7, + chain: "stellar" as SupportedChain, + priceUSD: dstToken?.priceUSD, + }; + + const route = this.routingService.buildRoute(srcToken, dstTokenInfo, solver.address, { + totalFeesUSD, + priceImpact, + estimatedFillTime: solver.avgFillTime + Math.floor(Math.random() * 30), + }); + + return { + solver: solver.address, + solverName: solver.name, + dstAmount: dstAmount.toString(), + fee: fee.toString(), + fillTime: solver.avgFillTime + Math.floor(Math.random() * 30), + expiresAt: Math.floor(Date.now() / 1000) + 60, + totalFeesUSD, + priceImpact, + route, + }; + }) + // nosemgrep: no-number-money -- sort comparator on bounded quote diffs only; amounts stay strings elsewhere. + .sort((a, b) => Number(BigInt(b.dstAmount) - BigInt(a.dstAmount))); + + if (quotes.length > 0) { + await this.intentsService.update(id, { quotedDstAmount: quotes[0].dstAmount }); + } + + const best = quotes[0] ?? null; + return { + quotes, + bestQuote: best, + srcChain: intent.srcChain, + srcTokenSymbol: srcToken?.symbol ?? "", + srcAmount: intent.srcAmount, + dstTokenSymbol: dstToken?.symbol ?? "", + estimatedFillTime: best?.fillTime ?? 0, + totalFeesUSD: best?.totalFeesUSD ?? 0, + priceImpact: best?.priceImpact ?? 0, + }; + } +} diff --git a/src/intents/intents.gateway.spec.ts b/src/intents/intents.gateway.spec.ts index e69de29b..8c23a751 100644 --- a/src/intents/intents.gateway.spec.ts +++ b/src/intents/intents.gateway.spec.ts @@ -0,0 +1,731 @@ +import { ConfigService } from "@nestjs/config"; +import { Keypair } from "@stellar/stellar-sdk"; +import { IntentsGateway, EventRingBuffer } from "./intents.gateway"; +import { IntentsService } from "./intents.service"; +import { StellarTxService } from "../soroban/stellar-tx.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { AppConfig } from "../config/configuration"; +import { InMemoryIntentsRepository } from "./intents.repository"; +import { logger } from "../common/logger"; +import { buildWsAuthMessage } from "../common/stellar-signature"; +import { ProtocolParamsService } from "../governance/params.service"; +import { IntentCapabilityIndex } from "./solver-intent-matcher"; +import { SolverRecord } from "../solvers/solvers.types"; + +/** + * Full `SolverRecord` stand-in for the WS auth path. + * + * The gateway compiles a capability predicate from the record, so a bare + * `{ address, isActive }` stub would throw on `supportedTokens.map(...)` the + * moment auth succeeds and take the whole worker down with it. + */ +function makeSolverRecord(address: string, overrides: Partial = {}): SolverRecord { + return { + address, + name: "test-solver", + bondAmount: "1000", + fillsCompleted: 0, + fillsFailed: 0, + totalVolume: "0", + avgFillTime: 0, + isActive: true, + registeredAt: 0, + lastActiveAt: 0, + supportedChains: ["ethereum"], + supportedTokens: ["USDC"], + ...overrides, + }; +} + +/** Stub capability index: the gateway only reads eligible intents from it. */ +function makeIntentIndex(): IntentCapabilityIndex { + return { + rebuild: jest.fn().mockResolvedValue(undefined), + addIntent: jest.fn(), + removeIntent: jest.fn(), + getEligibleFor: jest.fn().mockReturnValue([]), + } as unknown as IntentCapabilityIndex; +} +import { IntentFeedService } from "./feed/intent-feed.service"; + +jest.mock("../common/logger", () => ({ + logger: { + info: jest.fn(), + debug: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, +})); + +function makeIntentsService(): IntentsService { + const configService = { + get: jest.fn().mockReturnValue(false), + } as unknown as ConfigService; + const prismaService = { + intentAuditLog: { + create: jest.fn().mockResolvedValue({}), + findMany: jest.fn().mockResolvedValue([]), + }, + } as unknown as PrismaService; + const repo = new InMemoryIntentsRepository(); + const protocolParams = { + snapshotForChain: jest.fn().mockReturnValue({ version: 0, feeBps: 30, deadlineSeconds: 1800, fillWindowSeconds: 600, capturedAt: new Date().toISOString() }), + } as unknown as ProtocolParamsService; + return new IntentsService( + repo, + configService, + {} as StellarTxService, + prismaService, + protocolParams, + ); +} + +function makeSolversService() { + return { + get: jest.fn().mockResolvedValue(makeSolverRecord("GTEST")), + } as any; +} + +function makeIntentIndex(intentsService: IntentsService): IntentCapabilityIndex { + return new IntentCapabilityIndex(intentsService); +} + +function makeFeed( + intentsService: IntentsService, + solversService: ReturnType, + intentIndex: IntentCapabilityIndex, + ringBufferCapacity?: number, +): IntentFeedService { + const feed = new IntentFeedService(intentsService, solversService, intentIndex); + if (ringBufferCapacity !== undefined) feed.setRingBufferCapacity(ringBufferCapacity); + return feed; +} + +function createMockClient() { + const listeners: Record void> = {}; + return { + // Real `ws` sockets expose OPEN as an instance property (== 1); ConnectionState + // reads `socket.OPEN`, so the double must mirror that or every send is treated + // as "not open" and silently dropped. + OPEN: 1, + readyState: 1, // WebSocket.OPEN + bufferedAmount: 0, + send: jest.fn(), + ping: jest.fn(), + terminate: jest.fn(), + close: jest.fn(), + on: jest.fn((event: string, cb: (...args: unknown[]) => void) => { + listeners[event] = cb; + }), + off: jest.fn(), + _listeners: listeners, + // Helper: simulate an incoming message from the client + _emit: function (event: string, ...args: unknown[]) { + if (this._listeners[event]) this._listeners[event](...args); + }, + }; +} + +// ── EventRingBuffer unit tests ───────────────────────────────────────────── + +describe("EventRingBuffer", () => { + it("returns -1 for oldestSeq when empty", () => { + const buf = new EventRingBuffer(5); + expect(buf.oldestSeq()).toBe(-1); + }); + + it("returns 0 for latestSeq when empty", () => { + const buf = new EventRingBuffer(5); + expect(buf.latestSeq()).toBe(0); + }); + + it("tracks size", () => { + const buf = new EventRingBuffer(5); + buf.push({ seq: 1, type: "a" }); + buf.push({ seq: 2, type: "b" }); + expect(buf.size()).toBe(2); + }); + + it("evicts oldest when at capacity", () => { + const buf = new EventRingBuffer(3); + buf.push({ seq: 1, type: "a" }); + buf.push({ seq: 2, type: "b" }); + buf.push({ seq: 3, type: "c" }); + buf.push({ seq: 4, type: "d" }); // evicts seq=1 + expect(buf.oldestSeq()).toBe(2); + expect(buf.size()).toBe(3); + }); + + it("since returns only events after the given seq", () => { + const buf = new EventRingBuffer(10); + for (let i = 1; i <= 5; i++) buf.push({ seq: i, type: "e" }); + const result = buf.since(3); + expect(result.map((e) => e.seq)).toEqual([4, 5]); + }); + + it("since returns empty array when fromSeq >= latestSeq", () => { + const buf = new EventRingBuffer(10); + buf.push({ seq: 1, type: "e" }); + expect(buf.since(1)).toEqual([]); + expect(buf.since(99)).toEqual([]); + }); + + it("since returns all events when fromSeq < oldestSeq", () => { + const buf = new EventRingBuffer(3); + buf.push({ seq: 5, type: "e" }); + buf.push({ seq: 6, type: "e" }); + // fromSeq=1 is older than oldest (5), since() returns events with seq > 1 — all + const result = buf.since(1); + expect(result.map((e) => e.seq)).toEqual([5, 6]); + }); +}); + +// ── IntentsGateway heartbeat tests ──────────────────────────────────────── + +describe("IntentsGateway heartbeat", () => { + let gateway: IntentsGateway; + let intentsService: IntentsService; + let solversService: ReturnType; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + intentsService = makeIntentsService(); + solversService = makeSolversService(); + gateway = new IntentsGateway(intentsService, solversService, makeIntentIndex()); + const intentIndex = makeIntentIndex(intentsService); + const feed = makeFeed(intentsService, solversService, intentIndex); + gateway = new IntentsGateway(intentsService, solversService, intentIndex, feed); + }); + + afterEach(() => { + gateway.onModuleDestroy(); + jest.useRealTimers(); + }); + + it("marks new connections as alive", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + expect(gateway.getAliveCount()).toBe(1); + }); + + it("removes disconnected clients", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + expect(gateway.getAliveCount()).toBe(1); + gateway.handleDisconnect(client as unknown as import("ws").WebSocket); + expect(gateway.getAliveCount()).toBe(0); + }); + + it("terminates clients that do not respond to ping", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + jest.advanceTimersByTime(30_000); + + jest.advanceTimersByTime(30_000); + + expect(client.terminate).toHaveBeenCalled(); + expect(gateway.getAliveCount()).toBe(0); + }); + + it("keeps alive clients that respond with pong", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + jest.advanceTimersByTime(30_000); + + expect(client.ping).toHaveBeenCalled(); + expect(client.terminate).not.toHaveBeenCalled(); + + client._listeners.pong(); + + expect(gateway.getAliveCount()).toBe(1); + + jest.advanceTimersByTime(30_000); + + expect(client.terminate).not.toHaveBeenCalled(); + expect(gateway.getAliveCount()).toBe(0); + + client._listeners.pong(); + + expect(gateway.getAliveCount()).toBe(1); + }); + + it("cleans up interval on module destroy", () => { + gateway.onModuleDestroy(); + jest.advanceTimersByTime(60_000); + expect(true).toBe(true); + }); + + it("broadcasts to all alive subscribers (unfiltered)", async () => { + const c1 = createMockClient(); + const c2 = createMockClient(); + gateway.handleConnection(c1 as unknown as import("ws").WebSocket); + gateway.handleConnection(c2 as unknown as import("ws").WebSocket); + + // Wait for the async snapshot send to complete before clearing mocks + await Promise.resolve(); + + c1.send.mockClear(); + c2.send.mockClear(); + + await gateway.broadcast({ type: "test_event", data: 123 }); + + expect(c1.send).toHaveBeenCalledTimes(1); + expect(c2.send).toHaveBeenCalledTimes(1); + // Both payloads should contain the event type + const payload1 = JSON.parse(c1.send.mock.calls[0][0] as string); + expect(payload1.type).toBe("test_event"); + expect(typeof payload1.seq).toBe("number"); + }); + + it("accepts a valid solver auth message and rejects invalid signatures", async () => { + const keypair = Keypair.random(); + const client = createMockClient(); + const timestamp = Math.floor(Date.now() / 1000); + solversService.get = jest.fn().mockResolvedValue(makeSolverRecord(keypair.publicKey())); + + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + const message = buildWsAuthMessage(keypair.publicKey(), timestamp); + const signature = keypair.sign(Buffer.from(message, "utf8")).toString("base64"); + + await client._listeners.message(JSON.stringify({ type: "auth", solver: keypair.publicKey(), timestamp, signature })); + // auth_ok is followed by an eligible_snapshot frame, so assert the frame was + // sent rather than that it was the final one. ConnectionState passes a flush + // callback as a second argument, so match on the payload argument only. + expect(client.send.mock.calls.map((c: unknown[]) => c[0])).toContain( + JSON.stringify({ type: "auth_ok", method: "signature" }), + ); + + await client._listeners.message(JSON.stringify({ type: "auth", solver: keypair.publicKey(), timestamp, signature: "bad" })); + expect(client.send.mock.calls.map((c: unknown[]) => c[0])).toContain( + JSON.stringify({ type: "auth_error", reason: "invalid solver signature" }), + ); + }); +}); + +// ── IntentsGateway logging tests ────────────────────────────────────────── + +describe("IntentsGateway logging", () => { + let gateway: IntentsGateway; + let intentsService: IntentsService; + let solversService: ReturnType; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + intentsService = makeIntentsService(); + solversService = makeSolversService(); + gateway = new IntentsGateway(intentsService, solversService, makeIntentIndex()); + const intentIndex = makeIntentIndex(intentsService); + const feed = makeFeed(intentsService, solversService, intentIndex); + gateway = new IntentsGateway(intentsService, solversService, intentIndex, feed); + }); + + afterEach(() => { + gateway.onModuleDestroy(); + jest.useRealTimers(); + }); + + it("logs heartbeat started on construction", () => { + // The gateway now logs the backplane mode alongside the heartbeat banner. + expect(logger.info).toHaveBeenCalledWith(expect.stringMatching(/^ws heartbeat started/)); + }); + + it("logs connection with subscriber count", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + expect(logger.info).toHaveBeenCalledWith("ws client connected (subscribers=1)"); + }); + + it("logs disconnection with subscriber count", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + gateway.handleDisconnect(client as unknown as import("ws").WebSocket); + + expect(logger.info).toHaveBeenCalledWith("ws client disconnected (subscribers=0)"); + }); + + it("logs broadcast event type without payload", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + await gateway.broadcast({ type: "intent_created", intent: { id: "123", secret: "data" } }); + + // Sequencing/broadcast logging moved to the transport-agnostic feed service + // (issue #433); the log line must not include the event payload. + expect(logger.debug).toHaveBeenCalledWith( + expect.stringMatching(/feed broadcast type=intent_created/), + ); + }); + + it("logs heartbeat termination of dead client", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + jest.advanceTimersByTime(60_000); + + expect(logger.debug).toHaveBeenCalledWith( + "ws heartbeat terminated 1 dead client(s) (subscribers=0)", + ); + }); +}); + +// ── #257: Chain subscription filtering ──────────────────────────────────── + +describe("IntentsGateway — chain subscription filtering (#257)", () => { + let gateway: IntentsGateway; + let intentsService: IntentsService; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + intentsService = makeIntentsService(); + gateway = new IntentsGateway(intentsService, makeSolversService(), makeIntentIndex()); + const solversService = makeSolversService(); + const intentIndex = makeIntentIndex(intentsService); + const feed = makeFeed(intentsService, solversService, intentIndex); + gateway = new IntentsGateway(intentsService, solversService, intentIndex, feed); + }); + + afterEach(() => { + gateway.onModuleDestroy(); + jest.useRealTimers(); + }); + + it("responds with subscribed message when client sends valid subscribe", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + // Simulate incoming subscribe message + client._emit("message", Buffer.from(JSON.stringify({ type: "subscribe", chains: ["stellar", "ethereum"] }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const subscribed = calls.find((m) => m.type === "subscribed"); + expect(subscribed).toBeDefined(); + expect(subscribed.filter.chains).toEqual(expect.arrayContaining(["stellar", "ethereum"])); + }); + + it("strips invalid chain values from subscribe message", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + client._emit("message", Buffer.from(JSON.stringify({ + type: "subscribe", + chains: ["stellar", "invalid_chain", "STELLAR", 123], + }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const subscribed = calls.find((m) => m.type === "subscribed"); + expect(subscribed).toBeDefined(); + // Only "stellar" survives validation + expect(subscribed.filter.chains).toEqual(["stellar"]); + }); + + it("ignores subscribe message with missing chains field", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + // Should not crash and should not send subscribed + client._emit("message", Buffer.from(JSON.stringify({ type: "subscribe" }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const subscribed = calls.find((m) => m.type === "subscribed"); + expect(subscribed).toBeUndefined(); + }); + + it("ignores malformed JSON without crashing", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + // Should not throw + expect(() => { + client._emit("message", Buffer.from("not valid json{{{")); + }).not.toThrow(); + }); + + it("delivers intent_created only to subscribed chain clients", async () => { + const stellarClient = createMockClient(); + const ethClient = createMockClient(); + const allClient = createMockClient(); // no subscribe = receives all + + gateway.handleConnection(stellarClient as unknown as import("ws").WebSocket); + gateway.handleConnection(ethClient as unknown as import("ws").WebSocket); + gateway.handleConnection(allClient as unknown as import("ws").WebSocket); + + // Subscribe stellar client to stellar only + stellarClient._emit("message", Buffer.from(JSON.stringify({ type: "subscribe", chains: ["stellar"] }))); + // Subscribe eth client to ethereum only + ethClient._emit("message", Buffer.from(JSON.stringify({ type: "subscribe", chains: ["ethereum"] }))); + + stellarClient.send.mockClear(); + ethClient.send.mockClear(); + allClient.send.mockClear(); + + // Broadcast a stellar intent_created + await gateway.broadcast({ + type: "intent_created", + intent: { intentId: "abc", srcChain: "stellar", state: "open" }, + }); + + // stellarClient and allClient should receive it + const stellarCalls = stellarClient.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const ethCalls = ethClient.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const allCalls = allClient.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + + expect(stellarCalls.some((m) => m.type === "intent_created")).toBe(true); + expect(ethCalls.some((m) => m.type === "intent_created")).toBe(false); // filtered out + expect(allCalls.some((m) => m.type === "intent_created")).toBe(true); + }); + + it("delivers intent to all subscribers when chain is not resolvable", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client._emit("message", Buffer.from(JSON.stringify({ type: "subscribe", chains: ["stellar"] }))); + client.send.mockClear(); + + // Unknown type with no chain + await gateway.broadcast({ type: "system_announcement", message: "maintenance" }); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + expect(calls.some((m) => m.type === "system_announcement")).toBe(true); + }); + + it("unfiltered client (no subscribe) receives all events", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + await gateway.broadcast({ + type: "intent_created", + intent: { intentId: "xyz", srcChain: "ethereum", state: "open" }, + }); + await gateway.broadcast({ + type: "intent_created", + intent: { intentId: "abc", srcChain: "stellar", state: "open" }, + }); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const created = calls.filter((m) => m.type === "intent_created"); + expect(created).toHaveLength(2); + }); + + it("assigns increasing seq numbers to broadcast events", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + await gateway.broadcast({ type: "e1" }); + await gateway.broadcast({ type: "e2" }); + await gateway.broadcast({ type: "e3" }); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const seqs = calls.map((m: { seq: number }) => m.seq); + // seq values should be strictly increasing + for (let i = 1; i < seqs.length; i++) { + expect(seqs[i]).toBeGreaterThan(seqs[i - 1]); + } + }); +}); + +// ── #258: Event replay ──────────────────────────────────────────────────── + +describe("IntentsGateway — event replay (#258)", () => { + let gateway: IntentsGateway; + let intentsService: IntentsService; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + intentsService = makeIntentsService(); + gateway = new IntentsGateway(intentsService, makeSolversService(), makeIntentIndex()); + const solversService = makeSolversService(); + const intentIndex = makeIntentIndex(intentsService); + const feed = makeFeed(intentsService, solversService, intentIndex); + gateway = new IntentsGateway(intentsService, solversService, intentIndex, feed); + }); + + afterEach(() => { + gateway.onModuleDestroy(); + jest.useRealTimers(); + }); + + it("returns replay_start, replayed events, and replay_end for valid fromSeq", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + // Broadcast 3 events so they land in the ring buffer with seq 1, 2, 3 + await gateway.broadcast({ type: "e1" }); + await gateway.broadcast({ type: "e2" }); + await gateway.broadcast({ type: "e3" }); + + client.send.mockClear(); + + // Request replay from seq=1 (expect events with seq > 1 → seq 2 and 3) + client._emit("message", Buffer.from(JSON.stringify({ type: "replay", fromSeq: 1 }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const startMsg = calls.find((m) => m.type === "replay_start"); + const endMsg = calls.find((m) => m.type === "replay_end"); + const events = calls.filter((m) => m.type === "e2" || m.type === "e3"); + + expect(startMsg).toBeDefined(); + expect(startMsg.fromSeq).toBe(1); + expect(startMsg.count).toBe(2); + expect(events).toHaveLength(2); + expect(endMsg).toBeDefined(); + expect(endMsg.count).toBe(2); + }); + + it("returns replay_too_old when fromSeq has been evicted from the buffer", async () => { + // Use a tiny ring buffer (capacity 2) to force eviction + const tinyGateway = new IntentsGateway(intentsService, makeSolversService(), makeIntentIndex()); + // @ts-expect-error – accessing private field for test setup + tinyGateway.ringBuffer["capacity"] = 2; + const solversService = makeSolversService(); + const intentIndex = makeIntentIndex(intentsService); + const feed = makeFeed(intentsService, solversService, intentIndex, 2); + const tinyGateway = new IntentsGateway(intentsService, solversService, intentIndex, feed); + + const client = createMockClient(); + tinyGateway.handleConnection(client as unknown as import("ws").WebSocket); + + // Broadcast enough to evict seq=1 + await tinyGateway.broadcast({ type: "e1" }); // seq=1 + await tinyGateway.broadcast({ type: "e2" }); // seq=2 + await tinyGateway.broadcast({ type: "e3" }); // seq=3 — evicts seq=1 + + client.send.mockClear(); + + // seq=1 is now gone; oldest is seq=2. fromSeq=0 < oldest-1=1 → too_old + client._emit("message", Buffer.from(JSON.stringify({ type: "replay", fromSeq: 0 }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const tooOld = calls.find((m) => m.type === "replay_too_old"); + expect(tooOld).toBeDefined(); + expect(tooOld.fromSeq).toBe(0); + expect(typeof tooOld.oldestAvailableSeq).toBe("number"); + + tinyGateway.onModuleDestroy(); + }); + + it("returns replay with 0 events when fromSeq equals latest buffered seq", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + await gateway.broadcast({ type: "e1" }); // seq=1 + const lastSeq = 1; + + client.send.mockClear(); + + client._emit("message", Buffer.from(JSON.stringify({ type: "replay", fromSeq: lastSeq }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const startMsg = calls.find((m) => m.type === "replay_start"); + expect(startMsg).toBeDefined(); + expect(startMsg.count).toBe(0); + }); + + it("ignores replay with missing fromSeq", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + client._emit("message", Buffer.from(JSON.stringify({ type: "replay" }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + expect(calls.find((m) => m.type === "replay_start")).toBeUndefined(); + expect(calls.find((m) => m.type === "replay_too_old")).toBeUndefined(); + }); + + it("handles replay on an empty buffer (returns replay_start with count 0)", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + client.send.mockClear(); + + // Buffer is empty — oldestSeq() = -1, so the not-too-old path is taken + client._emit("message", Buffer.from(JSON.stringify({ type: "replay", fromSeq: 0 }))); + + const calls = client.send.mock.calls.map((c) => JSON.parse(c[0] as string)); + const startMsg = calls.find((m) => m.type === "replay_start"); + const endMsg = calls.find((m) => m.type === "replay_end"); + expect(startMsg).toBeDefined(); + expect(startMsg.count).toBe(0); + expect(endMsg).toBeDefined(); + }); + + it("pushes broadcast events into the ring buffer before sending", async () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + await gateway.broadcast({ type: "test_buffered" }); + + // The replay buffer now lives in the transport-agnostic feed service + // (issue #433); the gateway delegates replay to it. + // @ts-expect-error – accessing private for assertion + expect(gateway.feed.replaySince(0, null as never).events).toHaveLength(1); + }); +}); + +// ── #334: Heartbeat observability improvements ──────────────────────────── + +describe("IntentsGateway — heartbeat observability (#334)", () => { + let gateway: IntentsGateway; + let intentsService: IntentsService; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + intentsService = makeIntentsService(); + gateway = new IntentsGateway(intentsService, makeSolversService()); + }); + + afterEach(() => { + gateway.onModuleDestroy(); + jest.useRealTimers(); + }); + + it("heartbeatIntervalMs defaults to 30000", () => { + expect(gateway.heartbeatIntervalMs).toBe(30_000); + }); + + it("getLastTerminatedCount() returns 0 initially", () => { + expect(gateway.getLastTerminatedCount()).toBe(0); + }); + + it("getLastTerminatedCount() reflects terminated clients after heartbeat", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + // First tick: marks alive=false, sends ping + jest.advanceTimersByTime(30_000); + expect(gateway.getLastTerminatedCount()).toBe(0); + + // Second tick: client didn't pong → terminated + jest.advanceTimersByTime(30_000); + expect(gateway.getLastTerminatedCount()).toBe(1); + }); + + it("getZombieCount() returns count of clients that missed a ping but are not yet terminated", () => { + const client = createMockClient(); + gateway.handleConnection(client as unknown as import("ws").WebSocket); + + // Before first heartbeat: all clients are alive (alive=true), no zombies + expect(gateway.getZombieCount()).toBe(0); + + // After first heartbeat tick: alive is set to false for clients that didn't pong + jest.advanceTimersByTime(30_000); + expect(gateway.getZombieCount()).toBe(1); + + // After second tick: zombie is terminated, count back to 0 + jest.advanceTimersByTime(30_000); + expect(gateway.getZombieCount()).toBe(0); + }); +}); diff --git a/src/intents/intents.gateway.ts b/src/intents/intents.gateway.ts index e69de29b..c1eb2709 100644 --- a/src/intents/intents.gateway.ts +++ b/src/intents/intents.gateway.ts @@ -0,0 +1,1108 @@ +import { Inject, OnModuleDestroy, Optional } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway } from "@nestjs/websockets"; +import type { IncomingMessage } from "node:http"; +import { WebSocket } from "ws"; +import { IntentsService } from "./intents.service"; +import { SolversService } from "../solvers/solvers.service"; +import { MetricsService } from "../metrics/metrics.service"; +import { logger } from "../common/logger"; +import { SUPPORTED_CHAINS, SupportedChain } from "./intents.types"; +import { verifyStellarSignature, buildWsAuthMessage } from "../common/stellar-signature"; +import { buildMatchPredicate, IntentCapabilityIndex, SolverMatchPredicate } from "./solver-intent-matcher"; +import { + WS_MAX_FILTER_CHAINS, + WS_MAX_SUBSCRIPTIONS_PER_CONNECTION, +} from "../config/limits.config"; +import configuration, { AppConfig } from "../config/configuration"; +import { verifyHs256Jwt } from "../common/jwt"; +import { Backplane, SequencedEvent, WS_BACKPLANE } from "./backplane/backplane.types"; +import { MemoryBackplane } from "./backplane/memory.backplane"; +import { ConnectionState, resolveClientIp, EncodingFormat } from "./ws/connection-state"; +import { EncodingCache } from "./ws/encoding-cache"; +import { ConnectionState, resolveClientIp } from "./ws/connection-state"; +import { IntentFeedService } from "./feed/intent-feed.service"; +import { FeedClient, FeedFilter } from "./feed/feed.types"; +import { randomUUID } from "node:crypto"; + +export type { SequencedEvent } from "./backplane/backplane.types"; +export { EventRingBuffer } from "./event-ring-buffer"; + +/** Read WS_HEARTBEAT_INTERVAL_MS from the environment, falling back to 30 s. */ +function resolveHeartbeatIntervalMs(): number { + const parsed = Number(process.env.WS_HEARTBEAT_INTERVAL_MS); + return Number.isFinite(parsed) && parsed > 0 ? parsed : HEARTBEAT_INTERVAL_MS; +} + +/** + * WebSocket adapter over the transport-agnostic {@link IntentFeedService} + * (issue #433). + * + * The feed service owns sequencing, replay, filtering, delivery and connection + * accounting. This class owns only the WebSocket protocol: the upgrade + * handshake, the `subscribe` / `replay` / `auth` message frames, the + * solver-JWT handshake, and the ping/pong heartbeat. + */ +// maxPayload is enforced by `ws` itself (close 1009). Read from the +// environment because decorator options are evaluated at import time; +// handleMessage re-checks against the validated config. +@WebSocketGateway({ path: "/ws", maxPayload: configuration().ws.maxPayloadBytes }) +export class IntentsGateway + implements OnGatewayConnection, OnGatewayDisconnect, OnModuleDestroy +{ + /** Per-connection identity, rate-limit and outbound-queue state (issue #455). */ + private readonly connections = new Map(); + /** Maps a live WebSocket to its feed-service client wrapper. */ + private readonly feedClients = new Map(); + private readonly authenticatedSolver = new WeakMap(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private heartbeatTimer: any; + private readonly wsConfig: AppConfig["ws"]; + private readonly jwtSecret: string; + + /** Fan-out + global sequencing (issue #454): memory or Redis Streams. */ + private readonly backplane: Backplane; + /** Serialises local delivery so events reach clients in `seq` order. */ + private deliveryChain: Promise = Promise.resolve(); + + private nextSeq = 1; + + /** Configured heartbeat interval in milliseconds (default 30 000). */ + public readonly heartbeatIntervalMs: number; + + /** Number of connections terminated in the most recent heartbeat cycle. */ + private lastHeartbeatTerminatedCount = 0; + + /** Ring buffer storing the last REPLAY_BUFFER_SIZE broadcast events. */ + private readonly ringBuffer = new EventRingBuffer(REPLAY_BUFFER_SIZE); + + /** Encoding cache: serialize once per format, not per client (Activity 1). */ + private readonly encodingCache = new EncodingCache(REPLAY_BUFFER_SIZE); + + /** Graceful shutdown state (Activity 2). */ + private draining = false; + private drainStartedAt = 0; + + constructor( + private readonly intentsService: IntentsService, + private readonly solversService: SolversService, + private readonly intentIndex: IntentCapabilityIndex, + private readonly feed: IntentFeedService, + @Optional() private readonly metricsService?: MetricsService, + @Optional() config?: ConfigService, + @Optional() @Inject(WS_BACKPLANE) backplane?: Backplane, + ) { + const defaults = configuration(); + this.wsConfig = config?.get("ws", { infer: true }) ?? defaults.ws; + this.jwtSecret = config?.get("authJwtSecret", { infer: true }) ?? defaults.authJwtSecret; + this.heartbeatIntervalMs = resolveHeartbeatIntervalMs(); + this.heartbeatTimer = setInterval(() => this.heartbeat(), this.heartbeatIntervalMs); + this.backplane = this.createBackplane(); + if (this.backplane) { + this.backplane.subscribe((event) => { + const type = typeof event.type === "string" ? event.type : ""; + if (!type) return; + this.dispatchRemoteEvent(event as Record); + }); + } + logger.info(`ws heartbeat started (backplane=${this.feed.backplaneHealth().mode})`); + } + + /** Backplane health for /health (issue #454). */ + backplaneHealth() { + return this.feed.backplaneHealth(); + } + + /** + * Broadcast an event to every connected client (WS and SSE) on every replica. + * + * Activity 1: Uses encoding cache — serializes once per format, not per client. + * + * Delivery rules (evaluated in order): + * 1. Client is not OPEN → skip. + * 2. Client set wantAll=true → always deliver. + * 3. Client has a solver capability predicate: + * a. Event carries an inlined intent → apply predicate to that intent. + * b. Event is a state-transition (only intentId available) → deliver + * (we cannot efficiently look up the intent here; the solver would + * already have received the intent_created event through the filter). + * 4. Client has a plain chain filter (`chains != null`) → apply chain match. + * 5. No filter → full unfiltered feed (backward-compatible default). + */ + private deliverToMatchingSubscribers( + seq: number, + chain: SupportedChain | null, + event: { type: string; [key: string]: unknown }, + ) { + for (const [client, filter] of this.subscribers) { + if (client.readyState !== WebSocket.OPEN) continue; + + // Opt-out: solver requested full feed. + if (filter.wantAll) { + this.sendEncoded(client, seq, event); + continue; + } + + // Authenticated solver — apply capability predicate. + if (filter.solver !== null) { + const solverPredicate = filter.solver; + const inlinedIntent = (event as { intent?: unknown }).intent; + + // intent_created carries a full intent object we can test directly. + if (event.type === "intent_created" && inlinedIntent && typeof inlinedIntent === "object") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const matches = solverPredicate.matches(inlinedIntent as any); + if (matches) { + this.sendEncoded(client, seq, event); + try { this.metricsService?.incWsDelivered(solverPredicate.solverAddress); } catch { /* noop */ } + } else { + try { this.metricsService?.incWsFiltered(solverPredicate.solverAddress); } catch { /* noop */ } + } + continue; + } + + // State-transition events: the solver already filtered on intent_created, + // so we pass them through to keep the feed self-consistent. + this.sendEncoded(client, seq, event); + try { this.metricsService?.incWsDelivered(solverPredicate.solverAddress); } catch { /* noop */ } + continue; + } + + // No filter set → full unfiltered feed (backward-compatible default). + if (filter.chains === null) { + this.sendEncoded(client, seq, event); + continue; + } + + // Chain couldn't be resolved → deliver to everyone (safe default). + if (chain === null) { + this.sendEncoded(client, seq, event); + continue; + } + + // Only send if the event's chain is in this subscriber's filter. + if (filter.chains.has(chain)) { + this.sendEncoded(client, seq, event); + } + } + } + + handleConnection(client: WebSocket) { + this.subscribers.set(client, { + chains: null, + solver: null, + wantAll: false, + subscriptionCount: 0, + }); + /** + * Send an event to a client using its negotiated encoding format (Activity 1). + * + * Retrieves pre-serialized payload from encoding cache, avoiding redundant + * serialization work. At 10k connections with msgpack, this saves 9,999 + * msgpackEncode() calls per broadcast. + */ + private sendEncoded(client: WebSocket, seq: number, event: Record): void { + const state = this.connections.get(client); + if (!state) { + // Fallback for connections without state (shouldn't happen) + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify({ seq, ...event })); + } + return; + } + + const payload = this.encodingCache.get(seq, event, state.encoding); + const result = state.send(payload); + + if (result === "dropped_oldest") { + this.metricsService?.wsOutboundDropped.inc(); + } else if (result === "disconnected") { + this.metricsService?.wsSlowConsumerDisconnects.inc(); + logger.warn(`ws slow consumer disconnected (ip=${state.ip}, queue full)`); + this.removeSubscriber(client); + } + } + + /** + * Admits a connection (issue #455): enforces WS_MAX_CONNECTIONS and the + * per-IP limit (IP resolved through WS_TRUST_PROXY_HOPS), creates the + * per-connection state, and accepts an optional solver JWT from + * `?token=` or `Authorization: Bearer` (anonymous connections stay allowed). + * + * Activity 1: Negotiates encoding format via Sec-WebSocket-Protocol header. + * Activity 2: Rejects new connections when draining. + * Delegates to the feed service, which owns sequencing and delivery. This + * method exists so existing callers (controller, sweeper) keep working + * against the gateway's public surface. + */ + broadcast(event: { type: string; [key: string]: unknown }): Promise { + return this.feed.broadcast(event); + } + + private static isSupportedChain(value: unknown): value is SupportedChain { + return typeof value === "string" && (SUPPORTED_CHAINS as readonly string[]).includes(value); + } + + /** + * Admit a WebSocket connection (issue #455): enforces the shared connection + * limits through the feed service, creates the per-connection state, and + * accepts an optional solver JWT from `?token=` or `Authorization: Bearer`. + */ + handleConnection(client: WebSocket, request?: IncomingMessage) { + // Activity 2: Reject new connections during graceful shutdown + if (this.draining) { + client.close(1001, "Server draining"); + this.metricsService?.wsConnectionsRejected.inc({ reason: "draining" }); + return; + } + + const ip = resolveClientIp( + request?.socket?.remoteAddress, + request?.headers?.["x-forwarded-for"], + this.wsConfig.trustProxyHops, + ); + + const filter: FeedFilter = { chains: null, solver: null, wantAll: false, users: null, states: null, subscriptionCount: 0 }; + const state = new ConnectionState( + client, + ip, + { perSec: this.wsConfig.rateLimitPerSec, burst: this.wsConfig.rateLimitBurst }, + { + queueMax: this.wsConfig.outboundQueueMax, + bufferBytes: this.wsConfig.outboundBufferBytes, + policy: this.wsConfig.slowConsumerPolicy, + }, + ); + + // Activity 1: Negotiate encoding format from Sec-WebSocket-Protocol header + const protocols = request?.headers?.["sec-websocket-protocol"]; + const encoding = this.negotiateEncoding(protocols); + state.encoding = encoding; + + this.connections.set(client, state); + this.connectionsPerIp.set(ip, perIp + 1); + this.subscribers.set(client, { chains: null, solver: null, wantAll: false, subscriptionCount: 0 }); + this.alive.set(client, true); + const feedClient = new WsFeedClient(client, ip, state, this.metricsService); + const admission = this.feed.addClient(feedClient, filter); + if (!admission.ok) { + state.close(); + this.metricsService?.wsConnectionsRejected.inc({ reason: admission.reason ?? "max_connections" }); + client.close(1013, admission.reason === "per_ip" ? "Too many connections from this IP" : "Server at capacity"); + return; + } + + this.connections.set(client, state); + this.feedClients.set(client, feedClient); + this.metricsService?.incWsConnection(); + + client.on("message", (raw) => { + void this.handleMessage(client, raw); + }); + + client.on("pong", () => { + feedClient.alive = true; + }); + + client.on("error", () => { + this.removeSubscriber(client); + logger.debug( + `ws client error/drop — active subscribers: ${this.feed.connectionCount}`, + ); + }); + + const currentSeq = this.feed.currentSeq; + + this.send( + client, + JSON.stringify({ + type: "connected", + message: "Vortex intent stream", + seq: currentSeq, + encoding, + }), + ); + + // Send the initial snapshot asynchronously — the client receives it + // immediately after the "connected" message. + Promise.resolve(this.intentsService.getByState("open")) + .then((open) => { + this.send(client, JSON.stringify({ type: "snapshot", intents: open.slice(0, 20), seq: currentSeq })); + }) + .catch(() => { + /* snapshot failure is non-fatal — client can re-fetch via REST */ + }); + + const token = IntentsGateway.bearerToken(request); + if (token) void this.authenticateJwt(client, token); + + logger.info(`ws client connected (subscribers=${this.subscribers.size}, encoding=${encoding})`); + } + + /** + * Negotiate encoding format from Sec-WebSocket-Protocol header (Activity 1). + * + * Clients send: `Sec-WebSocket-Protocol: vortex.v1+msgpack` + * Server responds with the negotiated protocol in upgrade response. + * + * @returns "msgpack" if client requests it, otherwise "json" (default) + */ + private negotiateEncoding(protocols: string | string[] | undefined): EncodingFormat { + if (!protocols) return "json"; + const requested = Array.isArray(protocols) ? protocols : protocols.split(",").map(p => p.trim()); + if (requested.includes("vortex.v1+msgpack")) { + return "msgpack"; + } + return "json"; + logger.info(`ws client connected (subscribers=${this.feed.connectionCount})`); + } + + /** JWT from `?token=` or `Authorization: Bearer` on the upgrade request. */ + private static bearerToken(request?: IncomingMessage): string | null { + const auth = request?.headers?.authorization; + if (auth?.startsWith("Bearer ")) return auth.slice(7).trim(); + try { + return new URL(request?.url ?? "", "http://localhost").searchParams.get("token"); + } catch { + return null; + } + } + + /** + * Queues `payload` for `client` through its backpressure-aware state + * (issue #455), counting slow-consumer drops and disconnects. + */ + private send(client: WebSocket, payload: string): void { + const state = this.connections.get(client); + if (!state) { + if (client.readyState === WebSocket.OPEN) client.send(payload); + return; + } + const result = state.send(payload); + if (result === "dropped_oldest") { + this.metricsService?.wsOutboundDropped.inc(); + } else if (result === "disconnected") { + this.metricsService?.wsSlowConsumerDisconnects.inc(); + logger.warn(`ws slow consumer disconnected (ip=${state.ip}, queue full)`); + this.removeSubscriber(client); + } + } + + handleDisconnect(client: WebSocket) { + this.removeSubscriber(client); + logger.info(`ws client disconnected (subscribers=${this.feed.connectionCount})`); + } + + /** Drop a client from the feed service and keep the connection gauge honest. */ + private removeSubscriber(client: WebSocket): void { + const state = this.connections.get(client); + if (state) { + state.close(); + this.connections.delete(client); + } + const feedClient = this.feedClients.get(client); + if (feedClient) { + this.feed.removeClient(feedClient); + this.feedClients.delete(client); + } + this.authenticatedSolver.delete(client); + } + + /** + * Handle a single incoming WebSocket message from a client. + * + * Supported message types: + * - `{ type: "subscribe", chains?: string[], all?: boolean }` — set a + * per-connection filter or opt out of capability filtering with `all: true`. + * - `{ type: "replay", fromSeq: number }` — replay buffered events. + * - `{ type: "auth", solver, timestamp, signature }` — authenticate as a + * registered solver; installs a capability predicate and sends an + * auto-scoped snapshot of currently-eligible open intents. + */ + private async handleMessage(client: WebSocket, raw: import("ws").RawData): Promise { + if (client.readyState !== WebSocket.OPEN) return; + const size = Array.isArray(raw) + ? raw.reduce((n, b) => n + b.length, 0) + : (raw as Buffer | ArrayBuffer).byteLength; + if (size > this.wsConfig.maxPayloadBytes) { + client.close(1009, "Message too big"); + return; + } + + const state = this.connections.get(client); + if (state && !state.bucket.take()) { + state.violations += 1; + if (state.violations >= this.wsConfig.rateLimitMaxViolations) { + this.metricsService?.wsRateLimited.inc({ action: "disconnected" }); + client.close(1008, "Rate limit exceeded"); + return; + } + this.metricsService?.wsRateLimited.inc({ action: "rejected" }); + this.send(client, JSON.stringify({ type: "rate_limited", retryAfterMs: Math.ceil(1000 / this.wsConfig.rateLimitPerSec) })); + return; + } + + let parsed: unknown; + try { + parsed = JSON.parse(raw.toString()); + } catch { + return; + } + + if (typeof parsed !== "object" || parsed === null) return; + + const msg = parsed as Record; + + switch (msg.type) { + case "subscribe": + this.handleSubscribe(client, msg); + break; + case "replay": + this.handleReplay(client, msg); + break; + case "auth": + await this.handleAuth(client, msg); + break; + default: + break; + } + } + + /** + * Process a `{ type: "subscribe", chains?: string[], all?: boolean }` message. + * + * When `all: true` is present, the connection opts out of capability filtering + * and receives the complete unfiltered feed regardless of solver auth status. + * + * When `chains` is present, a per-connection chain filter is installed (this + * clears any existing solver capability predicate on the connection). + * Validates each chain value against `SUPPORTED_CHAINS` and stores only + * the valid subset. A subscribe message with no valid chains is treated as + * "subscribe to nothing" (the client will receive only chainless events). + * An entirely missing or non-array `chains` field is rejected silently + * without updating the existing filter. + * + * Issue #476: enforces two per-connection limits. + */ + private handleSubscribe(client: WebSocket, msg: Record): void { + const feedClient = this.feedClients.get(client); + if (!feedClient) return; + const current = this.feed.getFilter(feedClient); + if (!current) return; + + // all=true: opt out of capability filtering. + if (msg.all === true) { + const existing = this.subscribers.get(client) ?? { + chains: null, + solver: null, + wantAll: false, + subscriptionCount: 0, + }; + const existing = this.subscribers.get(client) ?? { chains: null, solver: null, wantAll: false, subscriptionCount: 0 }; + this.subscribers.set(client, { ...existing, wantAll: true }); + this.feed.updateClientFilter(feedClient, { ...current, wantAll: true }); + logger.debug("ws client opted out of capability filtering (all=true)"); + if (client.readyState === WebSocket.OPEN) { + this.send(client, JSON.stringify({ type: "subscribed", filter: { all: true } })); + } + return; + } + + if (!Array.isArray(msg.chains)) { + logger.debug("ws subscribe ignored: chains field missing or not an array"); + return; + } + + // ── Limit 1: max subscriptions per connection (issue #476) ─────────────── + const maxSubs = parseInt( + process.env.WS_MAX_SUBSCRIPTIONS ?? String(WS_MAX_SUBSCRIPTIONS_PER_CONNECTION), + 10, + ); + if (current.subscriptionCount >= maxSubs) { + logger.warn( + `ws subscribe_rejected: connection has reached the max subscription limit (${maxSubs})`, + ); + if (client.readyState === WebSocket.OPEN) { + this.send(client, + JSON.stringify({ + type: "subscribe_rejected", + reason: `Maximum subscription limit of ${maxSubs} reached for this connection`, + }), + ); + } + return; + } + + // ── Limit 2: max chain-filter values per subscribe message (issue #476) ── + const maxChains = parseInt( + process.env.WS_MAX_FILTER_CHAINS ?? String(WS_MAX_FILTER_CHAINS), + 10, + ); + const rawChains = msg.chains as unknown[]; + if (rawChains.length > maxChains) { + logger.warn( + `ws subscribe_rejected: chains array length ${rawChains.length} exceeds max ${maxChains}`, + ); + if (client.readyState === WebSocket.OPEN) { + this.send(client, + JSON.stringify({ + type: "subscribe_rejected", + reason: `chains array may contain at most ${maxChains} values`, + }), + ); + } + return; + } + + const validChains = rawChains.filter( + (c): c is SupportedChain => + typeof c === "string" && (SUPPORTED_CHAINS as readonly string[]).includes(c), + ); + + filter.chains = new Set(validChains); + filter.subscriptionCount += 1; + // An explicit chain filter replaces any solver capability predicate. + this.feed.updateClientFilter(feedClient, { + ...current, + chains: new Set(validChains), + solver: null, + wantAll: false, + subscriptionCount: current.subscriptionCount + 1, + }); + + logger.debug(`ws client subscribed to chains: ${validChains.join(", ") || "(none)"}`); + + if (client.readyState === WebSocket.OPEN) { + this.send(client, + JSON.stringify({ + type: "subscribed", + filter: { chains: validChains }, + }), + ); + } + } + + /** + * Process a `{ type: "replay", fromSeq: number }` message. + */ + private handleReplay(client: WebSocket, msg: Record): void { + const fromSeq = typeof msg.fromSeq === "number" ? msg.fromSeq : null; + if (fromSeq === null || !Number.isInteger(fromSeq) || fromSeq < 0) { + logger.debug("ws replay ignored: fromSeq missing or invalid"); + return; + } + + if (client.readyState !== WebSocket.OPEN) return; + + const feedClient = this.feedClients.get(client); + if (!feedClient) return; + + const result = this.feed.replaySince(fromSeq, feedClient); + + if (result.tooOld) { + this.send(client, + JSON.stringify({ + type: "replay_too_old", + fromSeq, + oldestAvailableSeq: result.oldestSeq, + }), + ); + logger.debug(`ws replay_too_old: fromSeq=${fromSeq} oldestAvailable=${result.oldestSeq}`); + return; + } + + this.send(client, + JSON.stringify({ + type: "replay_start", + fromSeq, + count: result.events.length, + }), + ); + + for (const event of result.events) { + if (client.readyState !== WebSocket.OPEN) break; + this.send(client, JSON.stringify(event)); + } + + if (client.readyState === WebSocket.OPEN) { + this.send(client, + JSON.stringify({ + type: "replay_end", + count: result.events.length, + }), + ); + } + + logger.debug(`ws replay complete: fromSeq=${fromSeq} count=${result.events.length}`); + } + + /** + * Authenticate a solver connection and install a capability predicate. + */ + private async handleAuth(client: WebSocket, payload: Record) { + if (typeof payload.token === "string") { + await this.authenticateJwt(client, payload.token); + return; + } + const solver = typeof payload.solver === "string" ? payload.solver : ""; + const timestamp = payload.timestamp; + const signature = typeof payload.signature === "string" ? payload.signature : ""; + + if (!solver || !signature || typeof timestamp !== "number") { + this.send(client, JSON.stringify({ type: "auth_error", reason: "auth payload requires solver, timestamp, and signature" })); + return; + } + + const now = Math.floor(Date.now() / 1000); + const skew = Math.abs(now - timestamp); + if (skew > 300) { + this.send(client, JSON.stringify({ type: "auth_error", reason: "stale or future auth timestamp" })); + return; + } + + const solverRecord = await this.solversService.get(solver); + if (!solverRecord || !solverRecord.isActive) { + this.send(client, JSON.stringify({ type: "auth_error", reason: "solver not registered or inactive" })); + return; + } + + try { + verifyStellarSignature(solver, buildWsAuthMessage(solver, timestamp), signature); + } catch { + this.send(client, JSON.stringify({ type: "auth_error", reason: "invalid solver signature" })); + return; + } + + await this.installSolver(client, solverRecord, "signature"); + } + + /** + * Authenticates with a solver JWT from the SEP-10 flow (issue #455 / #442). + */ + private async authenticateJwt(client: WebSocket, token: string): Promise { + const claims = verifyHs256Jwt(token, this.jwtSecret); + if (!claims) { + this.send(client, JSON.stringify({ type: "auth_error", reason: "invalid or expired token" })); + return; + } + const solverRecord = await this.solversService.get(claims.sub); + if (!solverRecord || !solverRecord.isActive) { + this.send(client, JSON.stringify({ type: "auth_error", reason: "solver not registered or inactive" })); + return; + } + await this.installSolver(client, solverRecord, "jwt"); + } + + /** Binds a verified solver identity to the connection and sends its scoped snapshot. */ + private async installSolver( + client: WebSocket, + solverRecord: NonNullable>>, + method: "signature" | "jwt", + ): Promise { + const solver = solverRecord.address; + const predicate = buildMatchPredicate(solverRecord); + this.authenticatedSolver.set(client, solver); + const authFilter = this.subscribers.get(client); + this.subscribers.set(client, { + chains: authFilter?.chains ?? null, + solver: predicate, + wantAll: authFilter?.wantAll ?? false, + subscriptionCount: authFilter?.subscriptionCount ?? 0, + }); + const state = this.connections.get(client); + if (state) state.identity = solver; + const feedClient = this.feedClients.get(client); + if (feedClient) { + const current = this.feed.getFilter(feedClient); + if (current) { + this.feed.updateClientFilter(feedClient, { ...current, solver: predicate }); + } + } + + this.send(client, JSON.stringify({ type: "auth_ok", method })); + + try { + const eligible = this.intentIndex.getEligibleFor(solverRecord); + this.send(client, JSON.stringify({ + type: "eligible_snapshot", + intents: eligible, + count: eligible.length, + })); + } catch { + // Non-fatal — solver can fall back to GET /solvers/:address/eligible-intents. + } + + // Use the predicate's normalized capability lists: a partially-populated + // solver record must not throw here and take down the auth path. + logger.info( + `ws solver auth ok: address=${solver} chains=${predicate.supportedChains.join(",")} tokens=${predicate.supportedTokens.join(",")}`, + ); + } + + /** + * Update the capability predicate for all live connections authenticated as + * the given solver address. + */ + async updateSolverPredicate(solverAddress: string): Promise { + await this.feed.updateSolverPredicate(solverAddress); + const solverRecord = await this.solversService.get(solverAddress); + if (!solverRecord) return; + + const predicate = buildMatchPredicate(solverRecord); + for (const [client, filter] of this.subscribers) { + if (this.authenticatedSolver.get(client) === solverAddress && filter.solver !== null) { + this.subscribers.set(client, { ...filter, solver: predicate }); + } + } + + logger.debug(`ws solver predicate updated for ${solverAddress}`); + } + + /** + * Resolve the source chain for an event payload. + */ + private async getEventChain( + event: { type: string; [key: string]: unknown }, + ): Promise { + if (event.type === "intent_created") { + const intent = event.intent as { srcChain?: string } | undefined; + const chain = intent?.srcChain; + if (chain && (SUPPORTED_CHAINS as readonly string[]).includes(chain)) { + return chain as SupportedChain; + } + return null; + } + + const lookupTypes = new Set([ + "intent_accepted", + "intent_filled", + "intent_cancelled", + "intent_expired", + "intent_slashed", + "auction_price", + ]); + + if (lookupTypes.has(event.type)) { + const intentId = typeof event.intentId === "string" ? event.intentId : null; + if (!intentId) return null; + + try { + const intent = await this.intentsService.get(intentId); + if (intent && (SUPPORTED_CHAINS as readonly string[]).includes(intent.srcChain)) { + return intent.srcChain as SupportedChain; + } + } catch { + // Lookup failure is non-fatal — deliver to all subscribers. + } + return null; + } + + return null; + } + + /** + * Broadcast an event to every client on every replica (issue #454). + * + * The backplane assigns the global sequence number and hands the event + * back to each replica's {@link deliver}. In memory mode this resolves after + * local delivery (unchanged behaviour); in redis mode it resolves once the + * event is queued, so request handlers never wait on Redis. + */ + async broadcast(event: { type: string; [key: string]: unknown }): Promise { + await this.backplane.publish(event); + } + + /** Chains deliveries so async chain lookups cannot reorder events. */ + private enqueueDelivery(event: SequencedEvent): Promise { + const run = this.deliveryChain.then(() => this.deliver(event)); + this.deliveryChain = run.catch((err: Error) => { + logger.error(`ws delivery failed: ${err.message}`); + }); + return this.deliveryChain; + } + + /** + * Push a sequenced event into the replay buffer, then deliver it to every + * subscriber whose filter matches. + * + * For authenticated solvers without `all=true`, only intents matching their + * capability predicate are delivered. State-transition events (no inlined + * intent) are always delivered to authenticated subscribers. + * + * Side-effects: + * - Updates the intent index for `intent_created` (add) and terminal-state + * events (remove), keeping the capability index fresh without a rebuild. + */ + private async deliver(sequencedEvent: SequencedEvent): Promise { + const enqueuedAt = Date.now(); + const { seq, ...event } = sequencedEvent; + + // Update the capability index before delivery so a racing replay or + // eligible-intents call sees fresh state. + this.updateIndexForEvent(sequencedEvent); + + // Push into replay buffer before sending. + this.ringBuffer.push(sequencedEvent); + + logger.debug(`ws broadcast type=${event.type} seq=${seq} subscribers=${this.subscribers.size}`); + + // Resolve the chain once — shared across all subscriber checks. + const eventChain = await this.getEventChain(sequencedEvent); + + // Activity 1: Pass seq + event separately so encoding cache can serialize + this.deliverToMatchingSubscribers(seq, eventChain, event); + + try { + this.metricsService?.observeWsDelivery((Date.now() - enqueuedAt) / 1000); + } catch { + // Metrics must never break broadcasts. + } + } + + /** Keep the IntentCapabilityIndex in sync with broadcast events. */ + private updateIndexForEvent(event: { type: string; [key: string]: unknown }): void { + try { + if (event.type === "intent_created") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const intent = (event as any).intent; + if (intent) this.intentIndex.addIntent(intent); + } else if ( + event.type === "intent_accepted" || + event.type === "intent_filled" || + event.type === "intent_cancelled" || + event.type === "intent_expired" || + event.type === "intent_slashed" + ) { + const intentId = typeof event.intentId === "string" ? event.intentId : null; + if (intentId) this.intentIndex.removeIntent(intentId); + } + } catch { + // Index update is best-effort — never break broadcasts. + } + } + + getAliveCount(): number { + let count = 0; + for (const [client, feedClient] of this.feedClients) { + if (feedClient.alive) count++; + } + return count; + } + + getSubscriberCount(): number { + return this.feed.connectionCount; + } + + /** Returns the current number of active WebSocket subscribers. */ + get subscriberCount(): number { + return this.feed.connectionCount; + } + + /** + * Check if the gateway is draining connections (Activity 2). + * Used by health indicators to flip readiness during shutdown. + */ + isDraining(): boolean { + return this.draining; + } + + private heartbeat() { + let terminated = 0; + for (const [client, feedClient] of this.feedClients) { + if (!feedClient.alive) { + client.terminate(); + this.removeSubscriber(client); + terminated++; + logger.debug( + `ws heartbeat terminated dead client (subscribers=${this.feed.connectionCount})`, + ); + continue; + } + + feedClient.alive = false; + if (client.readyState === WebSocket.OPEN) { + client.ping(); + } + } + this.lastHeartbeatTerminatedCount = terminated; + if (terminated > 0) { + logger.debug( + `ws heartbeat terminated ${terminated} dead client(s) (subscribers=${this.subscribers.size})`, + ); + } + continue; + } + + feedClient.alive = false; + if (client.readyState === WebSocket.OPEN) { + client.ping(); + } + } + this.lastHeartbeatTerminatedCount = terminated; + if (terminated > 0) { + logger.debug( + `ws heartbeat terminated ${terminated} dead client(s) (subscribers=${this.subscribers.size})`, + ); + } + } + + /** + * Returns the number of connections terminated in the most recent + * heartbeat cycle. Useful for presence stats and observability. + */ + getLastTerminatedCount(): number { + return this.lastHeartbeatTerminatedCount; + } + + /** + * Returns the number of connections that missed the last ping and are + * waiting to be terminated in the next heartbeat cycle ("zombies"). + */ + getZombieCount(): number { + let count = 0; + for (const client of this.subscribers.keys()) { + if (this.alive.get(client) === false) count++; + } + return count; + } + + async onModuleDestroy() { + // Activity 2: Graceful shutdown with connection draining + await this.startDraining(); + + if (this.heartbeatTimer) clearInterval(this.heartbeatTimer); + for (const [client, feedClient] of this.feedClients) { + this.removeSubscriber(client); + client.close(1001, "Server shutting down"); + } + } +} + +const HEARTBEAT_INTERVAL_MS = 30_000; + +/** + * WebSocket adapter that bridges a `ws` socket to the feed service's + * {@link FeedClient} interface (issue #433). + * + * `send` applies the per-connection backpressure policy (issue #455) and + * returns `false` only when the connection should be terminated. + */ +class WsFeedClient implements FeedClient { + alive = true; + readonly id: string; + readonly ip: string; + /** Number of `subscribe` messages this connection has sent (issue #476). */ + subscriptionCount = 0; + + constructor( + private readonly socket: WebSocket, + ip: string, + private readonly state: ConnectionState, + private readonly metricsService?: MetricsService, + ) { + this.id = randomUUID(); + this.ip = ip; + } + + send(payload: string): boolean { + const result = this.state.send(payload); + if (result === "dropped_oldest") { + this.metricsService?.wsOutboundDropped.inc(); + return true; + } + if (result === "disconnected") { + this.metricsService?.wsSlowConsumerDisconnects.inc(); + return false; + } + return true; + } + + close(): void { + this.state.close(); + if (this.socket.readyState === WebSocket.OPEN) { + this.socket.close(1001, "Server shutting down"); + } + } + + /** + * Begin graceful shutdown (Activity 2): notify all clients and close + * connections in batches within the termination grace period. + * + * Flow: + * 1. Set draining flag (rejects new connections) + * 2. Send server_draining event to all clients with resumeFrom seq + * 3. Close connections in batches with jittered delays + * 4. Background workers finish current batch (handled by IntentsSweeperService) + * + * Kubernetes terminationGracePeriodSeconds should be at least DRAIN_TIMEOUT_MS + 5s. + */ + async startDraining(): Promise { + if (this.draining) return; + + this.draining = true; + this.drainStartedAt = Date.now(); + + const drainTimeoutMs = parseInt(process.env.WS_DRAIN_TIMEOUT_MS ?? "25000", 10); + const currentSeq = this.backplane.health().lastSeq; + const clientCount = this.subscribers.size; + + logger.warn(`ws draining started: ${clientCount} clients, timeout=${drainTimeoutMs}ms`); + + // Notify all clients to reconnect with resume + const drainMessage = JSON.stringify({ + type: "server_draining", + resumeFrom: currentSeq, + reconnectAfterMs: this.jitter(1000, 5000), // Stagger reconnects + reason: "graceful_shutdown", + }); + + for (const [client] of this.subscribers) { + if (client.readyState === WebSocket.OPEN) { + try { + client.send(drainMessage); + } catch { + // Best effort notification + } + } + } + + // Close connections in batches to avoid thundering herd + const batchSize = Math.max(10, Math.ceil(clientCount / 10)); + const clients = Array.from(this.subscribers.keys()); + const batchDelayMs = Math.floor(drainTimeoutMs / Math.ceil(clientCount / batchSize)); + + for (let i = 0; i < clients.length; i += batchSize) { + const batch = clients.slice(i, i + batchSize); + + // Wait between batches + if (i > 0) { + await this.sleep(batchDelayMs); + } + + for (const client of batch) { + if (client.readyState === WebSocket.OPEN) { + client.close(1001, "Server draining"); + } + this.removeSubscriber(client); + } + + logger.info(`ws drain progress: ${Math.min(i + batchSize, clientCount)}/${clientCount} closed`); + } + + logger.warn(`ws draining complete: all clients closed`); + } + + /** + * Generate a jittered delay in [min, max] ms to stagger reconnects. + */ + private jitter(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + /** + * Promise-based sleep helper. + */ + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/src/intents/intents.module.ts b/src/intents/intents.module.ts index e69de29b..d31542a3 100644 --- a/src/intents/intents.module.ts +++ b/src/intents/intents.module.ts @@ -0,0 +1,100 @@ +import { Module, forwardRef } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { IntentsService } from "./intents.service"; +import { IntentsController } from "./intents.controller"; +import { IntentsSseController } from "./intents-sse.controller"; +import { IntentsGateway } from "./intents.gateway"; +import { IntentsSweeperService } from "./intents-sweeper.service"; +import { IntentsMaintenanceJobs } from "./intents-maintenance.jobs"; +import { INTENTS_REPOSITORY, InMemoryIntentsRepository } from "./intents.repository"; +import { PrismaIntentsRepository } from "./prisma-intents.repository"; +import { IntentCapabilityIndex } from "./solver-intent-matcher"; +import { backplaneProvider } from "./backplane/backplane.factory"; +import { backplaneHealthIndicator } from "./backplane/backplane-health.provider"; +import { IntentFeedService } from "./feed/intent-feed.service"; +import { Backplane, WS_BACKPLANE } from "./backplane/backplane.types"; +import { SolversModule } from "../solvers/solvers.module"; +import { SolversService } from "../solvers/solvers.service"; +import { MetricsService } from "../metrics/metrics.service"; +import { RoutingModule } from "../routing/routing.module"; +import { TokensModule } from "../tokens/tokens.module"; +import { SorobanModule } from "../soroban/soroban.module"; +import { AppConfig } from "../config/configuration"; +import { PrismaService } from "../prisma/prisma.service"; +import { GovernanceModule } from "../governance/governance.module"; +import { AbuseModule } from "../abuse/abuse.module"; +import { SignatureNonceService } from "../common/signature-nonce.service"; +import { EvmSignatureVerifier } from "../common/evm-signature"; +import { AuctionTickerService } from "../auctions/auction-ticker.service"; +import { FillVerifierService } from "../soroban/fill-verifier.service"; + +@Module({ + // Both SolversModule and SorobanModule import IntentsModule back, so both + // edges of each cycle must be deferred — a bare import resolves to `undefined` + // when the peer module is still mid-initialization (AppModule reaches + // SorobanModule through HealthModule before IntentsModule has finished). + // `forwardRef` on the SorobanModule import mirrors the one in SorobanModule: + // the two modules need each other (ShadowService here, IntentsService there). + imports: [ + forwardRef(() => SolversModule), + RoutingModule, + TokensModule, + forwardRef(() => SorobanModule), + GovernanceModule, + ], + controllers: [IntentsController, IntentsSseController], + controllers: [IntentsController], + providers: [ + // Select the persistence adapter based on INTENTS_PERSISTENCE env var. + // INTENTS_PERSISTENCE=prisma → PrismaIntentsRepository (production/staging) + // INTENTS_PERSISTENCE=memory → InMemoryIntentsRepository (default, dev/test) + { + provide: INTENTS_REPOSITORY, + inject: [ConfigService, PrismaService], + useFactory: (config: ConfigService, prisma: PrismaService) => { + const adapter = process.env.INTENTS_PERSISTENCE ?? "memory"; + if (adapter === "prisma") { + return new PrismaIntentsRepository(prisma); + } + return new InMemoryIntentsRepository(); + }, + }, + IntentsService, + SignatureNonceService, + EvmSignatureVerifier, + AuctionTickerService, + FillVerifierService, + IntentCapabilityIndex, + backplaneProvider, + // IntentFeedService is provided via a factory so its optional constructor + // parameters are not resolved positionally by Nest's injector. + { + provide: IntentFeedService, + inject: [ + IntentsService, + SolversService, + IntentCapabilityIndex, + { token: MetricsService, optional: true }, + ConfigService, + { token: WS_BACKPLANE, optional: true }, + ], + useFactory: ( + intentsService: IntentsService, + solversService: SolversService, + intentIndex: IntentCapabilityIndex, + metricsService: MetricsService | undefined, + config: ConfigService, + backplane: Backplane | undefined, + ) => + new IntentFeedService(intentsService, solversService, intentIndex, metricsService, config, backplane), + }, + IntentsGateway, + backplaneHealthIndicator, + IntentsSweeperService, + IntentsMaintenanceJobs, + // Note: EventIngestionService is provided by SorobanModule (imported above) + // and exported from there — no re-declaration needed here. + ], + exports: [IntentsService, IntentsGateway, IntentCapabilityIndex, IntentFeedService], +}) +export class IntentsModule {} diff --git a/src/intents/intents.service.shadow.spec.ts b/src/intents/intents.service.shadow.spec.ts index e69de29b..1e76bec9 100644 --- a/src/intents/intents.service.shadow.spec.ts +++ b/src/intents/intents.service.shadow.spec.ts @@ -0,0 +1,414 @@ +import { ConfigService } from "@nestjs/config"; +import { Keypair, scValToNative, xdr } from "@stellar/stellar-sdk"; +import { AppConfig } from "../config/configuration"; +import { MetricsService } from "../metrics/metrics.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { ProtocolParamsService } from "../governance/params.service"; +import { ShadowService, type ShadowObservationRequest } from "../soroban/shadow.service"; +import { StellarTxService } from "../soroban/stellar-tx.service"; +import { ProtocolParamsService } from "../governance/params.service"; +import { IntentsService } from "./intents.service"; +import { InMemoryIntentsRepository } from "./intents.repository"; +import { MutationResult, VersionConflict } from "./intents.repository"; +import { Intent } from "./intents.types"; + +/** Narrow a MutationResult to the Intent a successful mutation returns (issue #405). */ +function intentOf(result: MutationResult | undefined): Intent { + if (!result || result instanceof VersionConflict) throw new Error(`expected an intent, got ${JSON.stringify(result)}`); + return result; +} + + +/** + * Wiring tests for the shadow-mode divergence monitor at its real call sites + * (issue #401, acceptance criterion 1). + * + * `shadow.service.spec.ts` proves the monitor's own behaviour; this file proves + * the thing that actually matters for the cutover — that *every* lifecycle + * transition the off-chain path commits is reported, with the right + * `transition` label, the right `committed` verdict and contract-shaped + * arguments, and that adding the monitor does not show up in the request path. + */ + +const VALID_CONTRACT_ID = "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; + +function fakeConfig(): ConfigService { + const values: Record = { + onchainIntentsEnabled: false, + "stellar.settlementContractId": VALID_CONTRACT_ID, + }; + return { get: (path: string) => values[path] } as ConfigService; +} + +function fakeStellarTxService(): jest.Mocked { + return { invokeContract: jest.fn() } as unknown as jest.Mocked; +} + +function fakePrismaService(): PrismaService { + return { + intentAuditLog: { + create: jest.fn().mockResolvedValue({}), + findMany: jest.fn().mockResolvedValue([]), + }, + } as unknown as PrismaService; +} + +/** Protocol params are not what this file observes — a static snapshot suffices. */ +function fakeProtocolParamsService(): ProtocolParamsService { + return { + snapshotForChain: jest.fn().mockReturnValue({ + version: 0, + feeBps: 30, + deadlineSeconds: 1800, + fillWindowSeconds: 600, + capturedAt: new Date().toISOString(), + }), + } as unknown as ProtocolParamsService; +} + +/** + * Minimal stand-in for the monitor. + * + * `shouldObserve` is what `IntentsService` gates on, so a stub returning `true` + * is the "monitor on, fully sampled" configuration and `false` is "monitor off". + */ +function fakeShadowService(accepts = true) { + return { + observe: jest.fn(), + shouldObserve: jest.fn().mockReturnValue(accepts), + isEnabled: jest.fn().mockReturnValue(accepts), + }; +} + +function fakeMetricsService(): jest.Mocked { + return { incIntentStateTransition: jest.fn() } as unknown as jest.Mocked; +} + +interface Harness { + service: IntentsService; + shadow: ReturnType; + metrics: jest.Mocked; +} + +function makeService(options: { accepts?: boolean } = {}): Harness { + const shadow = fakeShadowService(options.accepts ?? true); + const metrics = fakeMetricsService(); + const service = new IntentsService( + new InMemoryIntentsRepository(), + fakeConfig(), + fakeStellarTxService(), + fakePrismaService(), + fakeProtocolParamsService(), + shadow as unknown as ShadowService, + metrics, + ); + return { service, shadow, metrics }; +} + +function createData(user: string) { + return { + user, + srcChain: "ethereum" as const, + srcToken: { + address: "0xabc", + symbol: "USDC", + name: "USD Coin", + decimals: 6, + chain: "ethereum" as const, + }, + srcAmount: "1000000", + dstToken: { contract: VALID_CONTRACT_ID, symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: Math.floor(Date.now() / 1000) + 1800, + }; +} + +/** The single observation recorded; fails the test if there was not exactly one. */ +function onlyObservation(shadow: ReturnType): ShadowObservationRequest { + expect(shadow.observe).toHaveBeenCalledTimes(1); + return shadow.observe.mock.calls[0][0] as ShadowObservationRequest; +} + +/** + * Read the contract arguments back as natives so the assertions describe the + * call the contract would receive rather than its XDR encoding. Addresses are + * stringified because `scValToNative` returns an `Address` object for them. + */ +function decodedArgs(args: xdr.ScVal[]): string[] { + return args.map((arg) => String(scValToNative(arg))); +} + +describe("IntentsService -> ShadowService wiring (#401)", () => { + it("reports accept with the solver address and the intent deadline", async () => { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + + const updated = await service.acceptIfOpen(intent.intentId, solver); + expect(intentOf(updated).state).toBe("accepted"); + + const observation = onlyObservation(shadow); + expect(observation.transition).toBe("accept"); + expect(observation.committed).toBe(true); + expect(observation.intentId).toBe(intent.intentId); + expect(observation.method).toBe("accept_intent"); + const args = decodedArgs(observation.args); + expect(args).toHaveLength(3); + expect(args[0]).toBe(intent.intentId); + expect(args[1]).toBe(solver); + }); + + it("reports accept as refused when the conditional write loses the race", async () => { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + await service.acceptIfOpen(intent.intentId, Keypair.random().publicKey()); + shadow.observe.mockClear(); + + // The intent is no longer open, so the guarded write must not commit — and + // the monitor has to see `committed: false`, because a contract that + // *would* have accepted it is exactly the divergence worth catching. + const loser = await service.acceptIfOpen(intent.intentId, Keypair.random().publicKey()); + expect(loser).toBeNull(); + expect(onlyObservation(shadow)).toMatchObject({ transition: "accept", committed: false }); + }); + + it("reports fill with the submitted amount and tx hash", async () => { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + await service.acceptIfOpen(intent.intentId, solver); + shadow.observe.mockClear(); + + const updated = await service.fillIfAccepted(intent.intentId, solver, { + fillAmount: "1000000", + txHash: "0xabc123", + }); + expect(intentOf(updated).state).toBe("filled"); + + const observation = onlyObservation(shadow); + expect(observation.transition).toBe("fill"); + expect(observation.committed).toBe(true); + expect(observation.method).toBe("fill_intent"); + expect(decodedArgs(observation.args)).toEqual([ + intent.intentId, + solver, + "1000000", + "0xabc123", + ]); + }); + + it("reports cancel against the user address", async () => { + const { service, shadow } = makeService(); + const user = Keypair.random().publicKey(); + const intent = await service.create(createData(user)); + + const updated = await service.cancelIfOpen(intent.intentId); + expect(intentOf(updated).state).toBe("cancelled"); + + const observation = onlyObservation(shadow); + expect(observation).toMatchObject({ + transition: "cancel", + committed: true, + method: "cancel_intent", + }); + expect(decodedArgs(observation.args)).toEqual([intent.intentId, user]); + }); + + it("reports expire against the intent deadline", async () => { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + + const updated = await service.expireIfOpen(intent.intentId); + expect(intentOf(updated).state).toBe("expired"); + + const observation = onlyObservation(shadow); + expect(observation).toMatchObject({ transition: "expire", committed: true }); + expect(observation.method).toBe("expire_intent"); + expect(decodedArgs(observation.args)[0]).toBe(intent.intentId); + }); + + it("reports slash against the solver and the penalty reason", async () => { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + await service.acceptIfOpen(intent.intentId, solver); + shadow.observe.mockClear(); + + const updated = await service.slashIfAccepted(intent.intentId, { + slashedAt: 1_700_000_000, + slashReason: "missed_fill_window", + }); + expect(intentOf(updated).state).toBe("slashed"); + + const observation = onlyObservation(shadow); + expect(observation).toMatchObject({ transition: "slash", committed: true }); + const args = decodedArgs(observation.args); + expect(args[0]).toBe(intent.intentId); + expect(args[1]).toBe(solver); + expect(args[2]).toBe("missed_fill_window"); + }); + + it("covers all five transitions the cutover would push on-chain", async () => { + const seen = new Set(); + + for (const transition of ["accept", "fill", "cancel", "expire", "slash"] as const) { + const { service, shadow } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + + if (transition === "accept") await service.acceptIfOpen(intent.intentId, solver); + if (transition === "fill") { + await service.acceptIfOpen(intent.intentId, solver); + await service.fillIfAccepted(intent.intentId, solver, { fillAmount: "1" }); + } + if (transition === "cancel") await service.cancelIfOpen(intent.intentId); + if (transition === "expire") await service.expireIfOpen(intent.intentId); + if (transition === "slash") { + await service.acceptIfOpen(intent.intentId, solver); + await service.slashIfAccepted(intent.intentId, { slashedAt: 1, slashReason: "late" }); + } + + for (const call of shadow.observe.mock.calls) { + seen.add((call[0] as ShadowObservationRequest).transition); + } + } + + expect([...seen].sort()).toEqual(["accept", "cancel", "expire", "fill", "slash"]); + }); + + it("does no shadow work at all when the monitor declines the transition", async () => { + const { service, shadow } = makeService({ accepts: false }); + const intent = await service.create(createData(Keypair.random().publicKey())); + + await service.acceptIfOpen(intent.intentId, Keypair.random().publicKey()); + + expect(shadow.shouldObserve).toHaveBeenCalled(); + expect(shadow.observe).not.toHaveBeenCalled(); + }); + + it("survives a monitor that throws, and still commits the transition", async () => { + const { service, shadow } = makeService(); + shadow.shouldObserve.mockImplementation(() => { + throw new Error("monitor exploded"); + }); + const intent = await service.create(createData(Keypair.random().publicKey())); + + // The monitor is observability. A bug in it must never turn into a failed + // intent transition. + const updated = await service.acceptIfOpen(intent.intentId, Keypair.random().publicKey()); + expect(intentOf(updated).state).toBe("accepted"); + expect(shadow.observe).not.toHaveBeenCalled(); + }); +}); + +describe("IntentsService -> MetricsService wiring (#481)", () => { + it("counts a creation into the funnel", async () => { + const { service, metrics } = makeService(); + await service.create(createData(Keypair.random().publicKey())); + + expect(metrics.incIntentStateTransition).toHaveBeenCalledWith("none", "open"); + }); + + it("counts every committed lifecycle edge and nothing else", async () => { + const { service, metrics } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + metrics.incIntentStateTransition.mockClear(); + + await service.acceptIfOpen(intent.intentId, solver); + // A second accept loses the race and must not be counted. + await service.acceptIfOpen(intent.intentId, Keypair.random().publicKey()); + + expect(metrics.incIntentStateTransition).toHaveBeenCalledTimes(1); + expect(metrics.incIntentStateTransition).toHaveBeenCalledWith("open", "accepted"); + }); + + it("counts cancellation, expiry and slashing from their own edges", async () => { + const { service, metrics } = makeService(); + const cancelled = await service.create(createData(Keypair.random().publicKey())); + const expired = await service.create(createData(Keypair.random().publicKey())); + const slashed = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + metrics.incIntentStateTransition.mockClear(); + + await service.cancelIfOpen(cancelled.intentId); + await service.expireIfOpen(expired.intentId); + await service.acceptIfOpen(slashed.intentId, solver); + await service.slashIfAccepted(slashed.intentId, { slashedAt: 1, slashReason: "late" }); + + expect(metrics.incIntentStateTransition.mock.calls.map((call) => call.slice(0, 2))).toEqual([ + ["open", "cancelled"], + ["open", "expired"], + ["open", "accepted"], + ["accepted", "slashed"], + ]); + }); + + it("counts a fill from accepted to filled", async () => { + const { service, metrics } = makeService(); + const intent = await service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + await service.acceptIfOpen(intent.intentId, solver); + metrics.incIntentStateTransition.mockClear(); + + await service.fillIfAccepted(intent.intentId, solver, { fillAmount: "1000000" }); + + expect(metrics.incIntentStateTransition).toHaveBeenCalledWith("accepted", "filled"); + }); +}); + +describe("IntentsService — shadow monitoring cost on the request path", () => { + /** p99 in milliseconds of `acceptIfOpen` over `iterations` calls. */ + async function measureP99( + service: IntentsService, + intentId: string, + solver: string, + iterations: number, + ): Promise { + const samples: number[] = []; + for (let i = 0; i < iterations; i += 1) { + const startedAt = process.hrtime.bigint(); + await service.acceptIfOpen(intentId, solver); + samples.push(Number(process.hrtime.bigint() - startedAt) / 1e6); + } + samples.sort((a, b) => a - b); + return samples[Math.floor(samples.length * 0.99)]; + } + + it("adds under 2 ms at p99 when the monitor is on (issue #401 budget)", async () => { + const off = makeService({ accepts: false }); + const on = makeService({ accepts: true }); + + const baseline = await off.service.create(createData(Keypair.random().publicKey())); + const monitored = await on.service.create(createData(Keypair.random().publicKey())); + const solver = Keypair.random().publicKey(); + + // Warm up both harnesses so the comparison is not dominated by first-call + // JIT on either side. + await measureP99(off.service, baseline.intentId, solver, 100); + await measureP99(on.service, monitored.intentId, solver, 100); + + const p99Off = await measureP99(off.service, baseline.intentId, solver, 500); + const p99On = await measureP99(on.service, monitored.intentId, solver, 500); + + // A delta, not an absolute: the interesting number for the issue is what + // the monitor costs, and both arms pay exactly the same repository work. + // + // This is a wall-clock measurement, so it is only meaningful relative to + // the noise floor of the machine it runs on. Assert the overhead is small + // compared to the baseline work, and skip the bound outright when the + // machine is too loaded for a sub-second measurement to be trustworthy + // (CI runners routinely exceed this and report a false regression). + const overhead = p99On - p99Off; + const noiseFloor = Math.max(p99Off, 0.05); + if (overhead > noiseFloor) { + // Both arms were dominated by scheduler/CPU contention, not by the + // monitor. Nothing about the monitor is being asserted here. + console.warn( + `[shadow] overhead assertion skipped: machine too noisy (off=${p99Off.toFixed(3)}ms on=${p99On.toFixed(3)}ms)`, + ); + return; + } + expect(overhead).toBeLessThan(Math.max(2, noiseFloor)); + }); +}); diff --git a/src/intents/intents.service.spec.ts b/src/intents/intents.service.spec.ts index e69de29b..83d3470c 100644 --- a/src/intents/intents.service.spec.ts +++ b/src/intents/intents.service.spec.ts @@ -0,0 +1,547 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ConfigService } from "@nestjs/config"; +import { Keypair } from "@stellar/stellar-sdk"; +import { AppConfig, CHAIN_FILL_WINDOW_DEFAULTS, DEFAULT_FILL_WINDOW_SECONDS } from "../config/configuration"; +import { StellarTxService } from "../soroban/stellar-tx.service"; +import { IntentsService } from "./intents.service"; +import { INTENTS_REPOSITORY, InMemoryIntentsRepository } from "./intents.repository"; +import { PrismaService } from "../prisma/prisma.service"; +import { ProtocolParamsService } from "../governance/params.service"; + +const VALID_CONTRACT_ID = "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; + +function fakeConfig(overrides: { onchainIntentsEnabled?: boolean; settlementContractId?: string } = {}) { + const values: Record = { + onchainIntentsEnabled: overrides.onchainIntentsEnabled ?? false, + "stellar.settlementContractId": overrides.settlementContractId ?? "", + }; + return { get: (path: string) => values[path] } as ConfigService; +} + +function fakeStellarTxService() { + return { invokeContract: jest.fn() } as unknown as jest.Mocked; +} + +function fakePrismaService(): PrismaService { + return { + intentAuditLog: { + create: jest.fn().mockResolvedValue({}), + findMany: jest.fn().mockResolvedValue([]), + }, + } as unknown as PrismaService; +} + +function fakeProtocolParamsService(): ProtocolParamsService { + return { + snapshotForChain: jest.fn().mockReturnValue({ + version: 0, + feeBps: 30, + deadlineSeconds: 1800, + fillWindowSeconds: 600, + capturedAt: new Date().toISOString(), + }), + getCurrent: jest.fn().mockReturnValue({ version: 0, feeBps: 30, chains: {}, maxExposureRatio: 0.05, slashAmount: "100000000", activeSinceLedger: 0, adoptedAt: new Date().toISOString() }), + getPending: jest.fn().mockReturnValue(null), + getHistory: jest.fn().mockReturnValue([]), + } as unknown as ProtocolParamsService; +} + +function makeService( + configOverrides: { onchainIntentsEnabled?: boolean; settlementContractId?: string } = {}, + stellarTx?: jest.Mocked, +) { + return new IntentsService( + new InMemoryIntentsRepository(), + fakeConfig(configOverrides), + stellarTx ?? fakeStellarTxService(), + fakePrismaService(), + fakeProtocolParamsService(), + ); +} + +function validCreateData() { + return { + user: Keypair.random().publicKey(), + srcChain: "ethereum" as const, + srcToken: { address: "0xabc", symbol: "USDC", name: "USD Coin", decimals: 6, chain: "ethereum" as const }, + srcAmount: "1000000", + dstToken: { contract: VALID_CONTRACT_ID, symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: Math.floor(Date.now() / 1000) + 1800, + }; +} + +async function buildService( + configOverrides: { onchainIntentsEnabled?: boolean; settlementContractId?: string } = {}, + stellarTxService?: jest.Mocked, +): Promise { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: INTENTS_REPOSITORY, + useClass: InMemoryIntentsRepository, + }, + { + provide: ConfigService, + useValue: fakeConfig(configOverrides), + }, + { + provide: StellarTxService, + useValue: stellarTxService ?? fakeStellarTxService(), + }, + { + provide: PrismaService, + useValue: fakePrismaService(), + }, + { + provide: ProtocolParamsService, + useValue: fakeProtocolParamsService(), + }, + IntentsService, + ], + }).compile(); + + return module.get(IntentsService); +} + +describe("IntentsService", () => { + let service: IntentsService; + + beforeEach(() => { + service = makeService(); + }); + + it("seeds 5 intents on construction", async () => { + expect(await service.getAll()).toHaveLength(5); + }); + + it("getAll returns intents sorted by createdAt descending", async () => { + const all = await service.getAll(); + for (let i = 1; i < all.length; i++) { + expect(all[i - 1].createdAt).toBeGreaterThanOrEqual(all[i].createdAt); + } + }); + + it("create adds an open intent with a generated id", async () => { + const before = (await service.getAll()).length; + const deadline = Math.floor(Date.now() / 1000) + 1800; + const intent = await service.create({ + user: "GTEST...0000", + srcChain: "ethereum", + srcToken: { address: "0xabc", symbol: "USDC", name: "USD Coin", decimals: 6, chain: "ethereum" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline, + }); + + expect(intent.state).toBe("open"); + expect(intent.intentId).toBeTruthy(); + expect(intent.deadline).toBe(deadline); + expect(await service.getAll()).toHaveLength(before + 1); + }); + + it("create defaults deadline to now + 1800 when omitted", async () => { + const before = Math.floor(Date.now() / 1000); + const intent = await service.create({ + user: "GTEST...0000", + srcChain: "ethereum", + srcToken: { address: "0xabc", symbol: "USDC", name: "USD Coin", decimals: 6, chain: "ethereum" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: undefined as unknown as number, + }); + + expect(intent.deadline).toBeGreaterThanOrEqual(before + 1800); + }); + + it("get returns undefined for an unknown id", async () => { + expect(await service.get("does-not-exist")).toBeUndefined(); + }); + + it("update mutates and returns the patched intent", async () => { + const [existing] = await service.getByState("open"); + const updated = await service.update(existing.intentId, { state: "accepted", solver: "SOLVER_X" }); + + expect(updated?.state).toBe("accepted"); + expect(updated?.solver).toBe("SOLVER_X"); + expect((await service.get(existing.intentId))?.state).toBe("accepted"); + }); + + it("update returns null for an unknown id", async () => { + expect(await service.update("does-not-exist", { state: "cancelled" })).toBeNull(); + }); + + it("getByUser is case-insensitive", async () => { + const [existing] = await service.getAll(); + const found = await service.getByUser(existing.user.toLowerCase()); + expect(found.some((i) => i.intentId === existing.intentId)).toBe(true); + }); + + it("getByState only returns intents in that state", async () => { + for (const intent of await service.getByState("filled")) { + expect(intent.state).toBe("filled"); + } + }); + + describe("acceptIfOpen", () => { + it("transitions an open intent to accepted and returns it", async () => { + const [open] = await service.getByState("open"); + const result = await service.acceptIfOpen(open.intentId, "SOLVER_X"); + + expect(result).not.toBeNull(); + expect(result!.state).toBe("accepted"); + expect(result!.solver).toBe("SOLVER_X"); + expect((await service.get(open.intentId))!.state).toBe("accepted"); + }); + + it("returns null for a non-existent intent", async () => { + expect(await service.acceptIfOpen("does-not-exist", "SOLVER_X")).toBeNull(); + }); + + it("returns null when the intent is already accepted", async () => { + const [accepted] = await service.getByState("accepted"); + expect(await service.acceptIfOpen(accepted.intentId, "SOLVER_X")).toBeNull(); + }); + + it("only the first caller wins under simulated concurrency", async () => { + const [open] = await service.getByState("open"); + const results = await Promise.all( + Array.from({ length: 10 }, (_, i) => + service.acceptIfOpen(open.intentId, `SOLVER_${i}`), + ), + ); + + const successes = results.filter((r) => r !== null); + expect(successes).toHaveLength(1); + expect(successes[0]!.state).toBe("accepted"); + }); + + // ----------------------------------------------------------------------- + // Per-chain fill-window tests (issue: chain-aware fill window) + // ----------------------------------------------------------------------- + + it("sets deadline to now + stellar fill window (120 s) for a stellar intent", async () => { + const now = Math.floor(Date.now() / 1000); + const intent = await service.create({ + user: "GTEST_STELLAR_CHAIN1", + srcChain: "stellar", + srcToken: { address: "native", symbol: "XLM", name: "Stellar Lumens", decimals: 7, chain: "stellar" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: now + 900, + }); + + const result = await service.acceptIfOpen(intent.intentId, "SOLVER_X"); + + expect(result).not.toBeNull(); + const expectedWindow = CHAIN_FILL_WINDOW_DEFAULTS["stellar"] ?? DEFAULT_FILL_WINDOW_SECONDS; + // Allow a 2-second tolerance for test execution time + expect(result!.deadline).toBeGreaterThanOrEqual(now + expectedWindow - 2); + expect(result!.deadline).toBeLessThanOrEqual(now + expectedWindow + 2); + }); + + it("sets deadline to now + ethereum fill window (1800 s) for an ethereum intent", async () => { + const now = Math.floor(Date.now() / 1000); + const intent = await service.create({ + user: "GTEST_ETHEREUM_CHAIN1", + srcChain: "ethereum", + srcToken: { address: "0xabc", symbol: "USDC", name: "USD Coin", decimals: 6, chain: "ethereum" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: now + 3600, + }); + + const result = await service.acceptIfOpen(intent.intentId, "SOLVER_X"); + + expect(result).not.toBeNull(); + const expectedWindow = CHAIN_FILL_WINDOW_DEFAULTS["ethereum"] ?? DEFAULT_FILL_WINDOW_SECONDS; + // Allow a 2-second tolerance for test execution time + expect(result!.deadline).toBeGreaterThanOrEqual(now + expectedWindow - 2); + expect(result!.deadline).toBeLessThanOrEqual(now + expectedWindow + 2); + }); + + it("stellar and ethereum accepted intents get distinct (non-equal) fill deadlines", async () => { + const now = Math.floor(Date.now() / 1000); + + const stellarIntent = await service.create({ + user: "GTEST_STELLAR_DIFF1", + srcChain: "stellar", + srcToken: { address: "native", symbol: "XLM", name: "Stellar Lumens", decimals: 7, chain: "stellar" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: now + 900, + }); + const ethIntent = await service.create({ + user: "GTEST_ETHEREUM_DIFF1", + srcChain: "ethereum", + srcToken: { address: "0xabc", symbol: "USDC", name: "USD Coin", decimals: 6, chain: "ethereum" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: now + 3600, + }); + + const stellarResult = await service.acceptIfOpen(stellarIntent.intentId, "SOLVER_STELLAR"); + const ethResult = await service.acceptIfOpen(ethIntent.intentId, "SOLVER_ETH"); + + expect(stellarResult).not.toBeNull(); + expect(ethResult).not.toBeNull(); + + // Ethereum solver gets a materially larger fill window than Stellar + expect(ethResult!.deadline).toBeGreaterThan(stellarResult!.deadline); + + // Confirm the windows match the config constants exactly (allowing 2 s clock drift) + const stellarWindow = CHAIN_FILL_WINDOW_DEFAULTS["stellar"] ?? DEFAULT_FILL_WINDOW_SECONDS; + const ethWindow = CHAIN_FILL_WINDOW_DEFAULTS["ethereum"] ?? DEFAULT_FILL_WINDOW_SECONDS; + expect(ethWindow).toBeGreaterThan(stellarWindow); // sanity-check on config + }); + + it("falls back to DEFAULT_FILL_WINDOW_SECONDS for an unknown chain", async () => { + const now = Math.floor(Date.now() / 1000); + const intent = await service.create({ + user: "GTEST_UNKNOWN_CHAIN01", + srcChain: "stellar", // create as valid chain, then patch for test + srcToken: { address: "native", symbol: "XLM", name: "Stellar Lumens", decimals: 7, chain: "stellar" }, + srcAmount: "1000000", + dstToken: { contract: "CTEST", symbol: "USDC", decimals: 7 }, + minDstAmount: "990000", + deadline: now + 3600, + }); + // Manually patch to an unknown chain to exercise the fallback + await service.update(intent.intentId, { srcChain: "unknown_chain" as never }); + + const result = await service.acceptIfOpen(intent.intentId, "SOLVER_X"); + + expect(result).not.toBeNull(); + expect(result!.deadline).toBeGreaterThanOrEqual(now + DEFAULT_FILL_WINDOW_SECONDS - 2); + expect(result!.deadline).toBeLessThanOrEqual(now + DEFAULT_FILL_WINDOW_SECONDS + 2); + }); + }); // end describe("acceptIfOpen") + + describe("fillIfAccepted", () => { + it("transitions an accepted intent to filled when solver matches", async () => { + const [accepted] = await service.getByState("accepted"); + const result = await service.fillIfAccepted(accepted.intentId, accepted.solver!, { + fillAmount: "100", + txHash: "test-hash", + filledAt: Math.floor(Date.now() / 1000), + }); + + expect(result).not.toBeNull(); + expect(result!.state).toBe("filled"); + expect(result!.fillAmount).toBe("100"); + }); + + it("returns null when solver does not match", async () => { + const [accepted] = await service.getByState("accepted"); + const result = await service.fillIfAccepted(accepted.intentId, "WRONG_SOLVER", { + fillAmount: "100", + }); + expect(result).toBeNull(); + }); + + it("returns null for a non-existent intent", async () => { + expect(await service.fillIfAccepted("nope", "SOLVER_X", {})).toBeNull(); + }); + + it("only the first caller wins under simulated concurrency", async () => { + const [accepted] = await service.getByState("accepted"); + const results = await Promise.all( + Array.from({ length: 10 }, () => + service.fillIfAccepted(accepted.intentId, accepted.solver!, { + fillAmount: "100", + txHash: "race-hash", + filledAt: Math.floor(Date.now() / 1000), + }), + ), + ); + + const successes = results.filter((r) => r !== null); + expect(successes).toHaveLength(1); + expect(successes[0]!.state).toBe("filled"); + }); + }); + + describe("on-chain registration (ONCHAIN_INTENTS_ENABLED)", () => { + it("stays fully in the repository when the flag is off, never touching StellarTxService", async () => { + const stellarTxService = fakeStellarTxService(); + const svc = makeService({ onchainIntentsEnabled: false }, stellarTxService); + + const intent = await svc.create(validCreateData()); + + expect(stellarTxService.invokeContract).not.toHaveBeenCalled(); + expect(await svc.get(intent.intentId)).toEqual(intent); + }); + + it("invokes the settlement contract and preserves the Intent shape when the flag is on", async () => { + const stellarTxService = fakeStellarTxService(); + stellarTxService.invokeContract.mockResolvedValue({ hash: "deadbeef", status: "SUCCESS" } as never); + const svc = makeService( + { onchainIntentsEnabled: true, settlementContractId: VALID_CONTRACT_ID }, + stellarTxService, + ); + + const data = validCreateData(); + const intent = await svc.create(data); + + expect(stellarTxService.invokeContract).toHaveBeenCalledTimes(1); + const call = stellarTxService.invokeContract.mock.calls[0][0]; + expect(call.contractId).toBe(VALID_CONTRACT_ID); + expect(call.method).toBe("create_intent"); + + // Response shape: the in-memory and on-chain paths return the same keys. + // usdValueAtCreate (#440) and paramsVersion (#500) are both stamped by + // persistNewIntent, so they appear on every newly created intent. + expect(Object.keys(intent).sort()).toEqual( + Object.keys({ + intentId: "", + user: "", + srcChain: "", + srcToken: "", + srcAmount: "", + dstToken: "", + minDstAmount: "", + state: "", + createdAt: 0, + deadline: 0, + paramsVersion: 0, + usdValueAtCreate: 0, + }).sort(), + ); + expect(await svc.get(intent.intentId)).toBeDefined(); + }); + + it("rejects with a clear error and does not create the intent when SETTLEMENT_CONTRACT_ID is unset", async () => { + const stellarTxService = fakeStellarTxService(); + const service = makeService({ onchainIntentsEnabled: true }, stellarTxService); + const before = (await service.getAll()).length; + + await expect(service.create(validCreateData())).rejects.toMatchObject({ + message: expect.stringContaining("SETTLEMENT_CONTRACT_ID"), + }); + expect(stellarTxService.invokeContract).not.toHaveBeenCalled(); + expect(await service.getAll()).toHaveLength(before); + }); + + it("rejects and does not create the intent when the on-chain call fails", async () => { + const stellarTxService = fakeStellarTxService(); + stellarTxService.invokeContract.mockRejectedValue(new Error("submission failed after 5 attempts")); + const svc = makeService( + { onchainIntentsEnabled: true, settlementContractId: VALID_CONTRACT_ID }, + stellarTxService, + ); + const before = (await svc.getAll()).length; + + await expect(svc.create(validCreateData())).rejects.toThrow(/settlement contract/i); + expect(await svc.getAll()).toHaveLength(before); + }); + }); + + // --------------------------------------------------------------------------- + // Audit trail (issue #217 / #62) + // --------------------------------------------------------------------------- + + describe("appendAuditEntry / getAuditLog", () => { + it("returns an empty array for an intent with no audit entries", () => { + expect(service.getAuditLog("no-such-intent")).toEqual([]); + }); + + it("appends a single entry and getAuditLog returns it", () => { + service.appendAuditEntry("intent-1", "cancelled", "USER_ADDR", "user cancelled"); + const log = service.getAuditLog("intent-1"); + expect(log).toHaveLength(1); + expect(log[0]).toMatchObject({ + toState: "cancelled", + actor: "USER_ADDR", + reason: "user cancelled", + }); + expect(log[0].timestamp).toBeTruthy(); // ISO timestamp + }); + + it("appends multiple entries in order and getAuditLog returns oldest-first", async () => { + service.appendAuditEntry("intent-2", "accepted", "SOLVER_A", "solver accepted"); + await new Promise((r) => setTimeout(r, 5)); // small gap so timestamps differ + service.appendAuditEntry("intent-2", "filled", "SOLVER_A", "solver filled"); + + const log = service.getAuditLog("intent-2"); + expect(log).toHaveLength(2); + expect(log[0].toState).toBe("accepted"); + expect(log[1].toState).toBe("filled"); + }); + + it("stores optional metadata in the entry", () => { + service.appendAuditEntry("intent-3", "expired", "system", "deadline passed", { + deadline: 1234567890, + sweepedAt: 1234567900, + }); + const log = service.getAuditLog("intent-3"); + expect(log[0].metadata).toEqual({ deadline: 1234567890, sweepedAt: 1234567900 }); + }); + + it("does not mix entries across different intentIds", () => { + service.appendAuditEntry("intent-A", "cancelled", "USER_A", "cancel A"); + service.appendAuditEntry("intent-B", "expired", "system", "expire B"); + + expect(service.getAuditLog("intent-A")).toHaveLength(1); + expect(service.getAuditLog("intent-B")).toHaveLength(1); + expect(service.getAuditLog("intent-A")[0].toState).toBe("cancelled"); + expect(service.getAuditLog("intent-B")[0].toState).toBe("expired"); + }); + + it("fires a DB write via PrismaService on each append (non-blocking)", async () => { + const prismaService = { + intentAuditLog: { + create: jest.fn().mockResolvedValue({}), + findMany: jest.fn().mockResolvedValue([]), + }, + } as unknown as PrismaService; + const svc = new IntentsService(new InMemoryIntentsRepository(), fakeConfig(), fakeStellarTxService(), prismaService, fakeProtocolParamsService()); + + svc.appendAuditEntry("intent-db", "slashed", "system", "missed fill", { foo: "bar" }); + + // The DB write is fire-and-forget — wait one tick for the promise chain + await new Promise((r) => setImmediate(r)); + + const mockPrisma = prismaService as unknown as { + intentAuditLog: { create: jest.Mock }; + }; + expect(mockPrisma.intentAuditLog.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + intentId: "intent-db", + toState: "slashed", + actor: "system", + reason: "missed fill", + }), + }), + ); + }); + + it("does NOT throw when the DB write fails — logs an error but returns normally", async () => { + const prismaService = { + intentAuditLog: { + create: jest.fn().mockRejectedValue(new Error("DB is down")), + findMany: jest.fn().mockResolvedValue([]), + }, + } as unknown as PrismaService; + const svc = new IntentsService(new InMemoryIntentsRepository(), fakeConfig(), fakeStellarTxService(), prismaService, fakeProtocolParamsService()); + + // Should not throw synchronously + expect(() => + svc.appendAuditEntry("intent-fail", "expired", "system", "deadline"), + ).not.toThrow(); + + // In-memory log still has the entry + expect(svc.getAuditLog("intent-fail")).toHaveLength(1); + + // Wait for the rejected promise — should not propagate + await new Promise((r) => setImmediate(r)); + // No unhandled rejection here (jest would fail the test if one occurred) + }); + }); +}); diff --git a/src/intents/intents.types.ts b/src/intents/intents.types.ts index e69de29b..587ad940 100644 --- a/src/intents/intents.types.ts +++ b/src/intents/intents.types.ts @@ -0,0 +1,151 @@ +import type { DutchAuction } from "../auctions/dutch"; + +/** + * Single source of truth for every chain the protocol recognises. + * `SupportedChain` is derived from this tuple so all three consumers + * (intents.types.ts, create-intent.dto.ts, tokens.data.ts) stay in sync + * automatically — see issue #128. + */ +export const SUPPORTED_CHAINS = [ + "stellar", + "ethereum", + "base", + "polygon", + "arbitrum", + "optimism", + "avalanche", +] as const; + +export type SupportedChain = (typeof SUPPORTED_CHAINS)[number]; + +/** + * The Stellar chain identifier, named for readability at call sites that would + * otherwise repeat the literal. + * + * Distinct from the Soroban *network* ("testnet" / "mainnet" / + * "futurenet"), which selects an RPC endpoint. Kill-switch scopes and intent + * records are addressed by chain, not by network, so anything matching against + * a chain must use this value. + */ +export const STELLAR_CHAIN = "stellar" satisfies SupportedChain; + +/** + * A single entry in the append-only audit log for an intent. + * Every state transition — cancel, expire, accept, fill — appends one entry. + * Once persistence lands (issue #36) this will be written to an `intent_audit_log` + * table; for now it lives in-memory alongside the intent map. + */ +export interface IntentAuditEntry { + /** ISO-8601 UTC timestamp of the transition. */ + timestamp: string; + /** State the intent moved INTO. */ + toState: IntentState; + /** Actor who triggered the transition: a user address, solver address, or "system". */ + actor: string; + /** Human-readable explanation, e.g. "user cancelled", "deadline passed". */ + reason: string; + /** Optional extra data (fill amount, tx hash, …). */ + metadata?: Record; +} + +/** + * Single source of truth for every state an intent can be in. + * `IntentState` is derived from this tuple so DTO validators (`@IsIn`), + * Swagger `enum:` annotations, and type-checking all stay in sync + * automatically — mirrors how `SUPPORTED_CHAINS` is defined above (issue #270). + */ +export const INTENT_STATES = [ + "open", + "accepted", + "filled", + "cancelled", + "expired", + "slashed", +] as const; + +export type IntentState = (typeof INTENT_STATES)[number]; + +export interface TokenInfo { + address: string; + symbol: string; + name: string; + decimals: number; + chain: SupportedChain; + logoURI?: string; + priceUSD?: number; +} + +export interface StellarToken { + contract: string; + symbol: string; + decimals: number; + priceUSD?: number; +} + +export interface Intent { + intentId: string; + user: string; + srcChain: SupportedChain; + srcToken: TokenInfo; + srcAmount: string; // bigint as string + dstToken: StellarToken; + minDstAmount: string; + auction?: DutchAuction; + acceptedDstAmount?: string; + quotedDstAmount?: string; // best quote from solvers + solver?: string; + state: IntentState; + createdAt: number; + deadline: number; + filledAt?: number; + fillAmount?: string; + feeAmount?: string; // realized protocol fee in dst token base units + txHash?: string; // fill tx on Stellar + fillVerificationState?: "pending" | "verified" | "rejected"; + fillVerificationReason?: string; + fillVerifiedAt?: string; + slashedAt?: number; + slashReason?: string; + /** + * Snapshot of the governance-controlled protocol parameters that were active + * when this intent was created. Used to evaluate fee/window terms for + * in-flight intents even after a governance update changes the live values. + * Absent on intents created before issue #500 was deployed. + */ + paramsVersion?: number; + /** + * USD value of `srcAmount` at creation time, computed from the resolved + * source-token price. Powers the `minAmountUsd` / `maxAmountUsd` filters + * and USD sorting (issue #440). `undefined` when the token price was + * unknown at creation — historical rows are never backfilled with + * fabricated values. + */ + usdValueAtCreate?: number; +} + +export interface Quote { + intentId: string; + solver: string; + dstAmount: string; + fee: string; // protocol fee in dst token + fillTime: number; // estimated seconds + expiresAt: number; +} + +export interface RouteStep { + type: "bridge" | "swap" | "transfer"; + protocol: string; + fromChain: string; + toChain: string; + fromToken: TokenInfo; + toToken: TokenInfo; + estimatedTime: number; + estimatedGas: string; +} + +export interface Route { + steps: RouteStep[]; + totalTime: number; // seconds + totalFeesUSD: number; + priceImpact: number; +} diff --git a/src/intents/prisma-intents.repository.ts b/src/intents/prisma-intents.repository.ts index e69de29b..25a9e3be 100644 --- a/src/intents/prisma-intents.repository.ts +++ b/src/intents/prisma-intents.repository.ts @@ -0,0 +1,486 @@ +import { Injectable } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { IIntentsRepository, IntentSearchQuery, IntentSearchResult } from "./intents.repository"; +import { Intent, IntentState, StellarToken, TokenInfo } from "./intents.types"; +import { IntentState as PrismaIntentState, Prisma } from "@prisma/client"; + +/** PrismaService, or the client handed to a `$transaction` callback (issue #396). */ +export type IntentsPrismaClient = PrismaService | Prisma.TransactionClient; + +/** + * Prisma-backed implementation of IIntentsRepository. + * + * All mutating operations that must be race-free (`acceptIfOpen`, + * `fillIfAccepted`) use a single conditional `updateMany` call so the + * database enforces the state guard atomically — no separate read-then-write. + * + * Bigint amounts (srcAmount, minDstAmount, fillAmount, quotedDstAmount) are + * stored and returned as strings per the project's bigint-as-string convention + * (see CONTRIBUTING.md). JSON columns (srcToken, dstToken) are cast back to + * their TypeScript types on the way out. + */ +@Injectable() +export class PrismaIntentsRepository implements IIntentsRepository { + constructor(private readonly prisma: IntentsPrismaClient) {} + + async save(intent: Intent): Promise { + const data = this.toDbData(intent); + await this.prisma.intent.upsert({ + where: { intentId: intent.intentId }, + create: { ...data, intentId: intent.intentId }, + update: data, + }); + return intent; + } + + async findById(id: string): Promise { + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : undefined; + } + + async findAll(): Promise { + const rows = await this.prisma.intent.findMany({ + orderBy: { createdAt: "desc" }, + }); + return rows.map((r) => this.fromRow(r)); + } + + async findByState(state: IntentState): Promise { + const rows = await this.prisma.intent.findMany({ + where: { state: this.toPrismaState(state) }, + orderBy: { createdAt: "desc" }, + }); + return rows.map((r) => this.fromRow(r)); + } + + async findByUser(user: string): Promise { + // Postgres is case-sensitive; normalise the address comparison in-query. + const rows = await this.prisma.intent.findMany({ + where: { user: { equals: user, mode: "insensitive" } }, + orderBy: { createdAt: "desc" }, + }); + return rows.map((r) => this.fromRow(r)); + } + + /** + * Advanced search (issue #440) — filtering, sorting and pagination pushed + * into the database query. + * + * Scalar filters use Prisma's typed where input; the JSONB token-symbol + * filters use raw SQL (`lower(token->>'symbol') = lower($1)`) so they can + * use the expression indexes created in the migration and stay + * case-insensitive. Sorting and pagination are applied in the same query so + * no supported combination falls back to a sequential scan + app-side slice. + */ + async search(query: IntentSearchQuery): Promise { + const where: Prisma.IntentWhereInput = {}; + + if (query.state !== undefined) where.state = this.toPrismaState(query.state); + if (query.chain !== undefined) where.srcChain = query.chain as Prisma.IntentWhereInput["srcChain"]; + if (query.user !== undefined) where.user = { equals: query.user, mode: "insensitive" }; + if (query.solver !== undefined) where.solver = { equals: query.solver, mode: "insensitive" }; + + if (query.minAmountUsd !== undefined && query.maxAmountUsd !== undefined) { + where.usdValueAtCreate = { gte: query.minAmountUsd, lte: query.maxAmountUsd }; + } else if (query.minAmountUsd !== undefined) { + where.usdValueAtCreate = { gte: query.minAmountUsd }; + } else if (query.maxAmountUsd !== undefined) { + where.usdValueAtCreate = { lte: query.maxAmountUsd }; + } + + if (query.createdFrom !== undefined && query.createdTo !== undefined) { + where.createdAt = { gte: query.createdFrom, lte: query.createdTo }; + } else if (query.createdFrom !== undefined) { + where.createdAt = { gte: query.createdFrom }; + } else if (query.createdTo !== undefined) { + where.createdAt = { lte: query.createdTo }; + } + + // JSONB token-symbol filters — raw SQL so the expression indexes apply. + const raw: Prisma.Sql[] = []; + if (query.srcToken !== undefined) { + raw.push(Prisma.sql`lower(src_token->>'symbol') = lower(${query.srcToken})`); + } + if (query.dstToken !== undefined) { + raw.push(Prisma.sql`lower(dst_token->>'symbol') = lower(${query.dstToken})`); + } + if (raw.length > 0) { + (where as { AND: unknown }).AND = raw; + } + + // Sorting — default createdAt desc (preserves pre-existing behaviour). + const [dimension, direction] = (query.sort ?? "created:desc").split(":"); + const dir = direction === "asc" ? ("asc" as const) : ("desc" as const); + let orderBy: Prisma.IntentOrderByWithRelationInput; + switch (dimension) { + case "deadline": + orderBy = { deadline: dir }; + break; + case "usd": + orderBy = { usdValueAtCreate: dir }; + break; + case "created": + default: + orderBy = { createdAt: dir }; + } + + const offset = query.offset ?? 0; + const limit = query.limit ?? 20; + + const [rows, total] = await Promise.all([ + this.prisma.intent.findMany({ where, orderBy, skip: offset, take: limit }), + this.prisma.intent.count({ where }), + ]); + + return { intents: rows.map((r) => this.fromRow(r)), total }; + } + + async update(id: string, patch: Partial): Promise { + try { + const row = await this.prisma.intent.update({ + where: { intentId: id }, + data: this.toDbPatch(patch), + }); + return this.fromRow(row); + } catch (err) { + // P2025 = Record to update not found + if ((err as Prisma.PrismaClientKnownRequestError).code === "P2025") return null; + throw err; + } + } + + async delete(id: string): Promise { + try { + await this.prisma.intent.delete({ where: { intentId: id } }); + return true; + } catch (err) { + if ((err as Prisma.PrismaClientKnownRequestError).code === "P2025") return false; + throw err; + } + } + + /** + * Atomically accept an intent only when it is currently `open` AND its + * deadline is still in the future (issue #473). + * + * Uses a single `updateMany` with a compound WHERE clause so the database + * enforces the state + deadline guards — zero rows updated means another + * solver already won the race or the sweeper already expired the intent. + * + * Lock ordering: callers enforcing per-solver caps must hold the solver + * advisory lock (`pg_advisory_xact_lock`) BEFORE calling this method. + */ + async acceptIfOpen( + id: string, + solver: string, + newDeadline: number, + now?: number, + acceptedDstAmount?: string, + ): Promise { + const nowSec = now ?? Math.floor(Date.now() / 1000); + const result = await this.prisma.intent.updateMany({ + where: { intentId: id, state: PrismaIntentState.open, deadline: { gt: nowSec } }, + data: { + state: PrismaIntentState.accepted, + solver, + deadline: newDeadline, + ...(acceptedDstAmount !== undefined ? { acceptedDstAmount } : {}), + }, + }); + + if (result.count === 0) return null; // not found, already taken, or expired + + // Fetch the updated row to return the full intent shape. + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Acquire a transaction-scoped advisory lock for a solver key (issue #473). + * + * Must be called inside a `$transaction` callback to serialize per-solver + * cap checks across replicas. Lock ordering: solver lock BEFORE any intent + * row write, released automatically at transaction end. No-op fallback when + * the Prisma client does not expose `$executeRaw` (e.g. unit tests). + */ + async acquireSolverLock(solver: string): Promise { + const client = this.prisma as unknown as { + $executeRaw?: (q: TemplateStringsArray, ...v: unknown[]) => Promise; + }; + if (typeof client.$executeRaw !== "function") return; + await client.$executeRaw`SELECT pg_advisory_xact_lock(hashtext(${solver}))`; + } + + /** Count open/accepted intents for a user with a single COUNT query. */ + async countOpenByUser(user: string): Promise { + return this.prisma.intent.count({ + where: { + user: { equals: user, mode: "insensitive" }, + state: { in: [PrismaIntentState.open, PrismaIntentState.accepted] }, + }, + }); + } + + /** + * Atomically fill an intent only when it is currently `accepted` by the + * specified solver AND the fill window has not elapsed (issue #473). + * + * Uses a single `updateMany` with a compound WHERE clause — zero rows + * updated means the intent was not in the expected state, is assigned to a + * different solver, or the deadline passed (sweeper wins). + */ + async fillIfAccepted( + id: string, + solver: string, + patch: Omit, "state" | "solver">, + now?: number, + ): Promise { + const nowSec = now ?? Math.floor(Date.now() / 1000); + const result = await this.prisma.intent.updateMany({ + where: { + intentId: id, + state: PrismaIntentState.accepted, + solver, + deadline: { gt: nowSec }, + }, + data: { + state: PrismaIntentState.filled, + ...(patch.filledAt !== undefined ? { filledAt: patch.filledAt } : {}), + ...(patch.fillAmount !== undefined ? { fillAmount: patch.fillAmount } : {}), + ...(patch.feeAmount !== undefined + ? { feeAmount: patch.feeAmount as string } + : {}), + ...(patch.txHash !== undefined ? { txHash: patch.txHash } : {}), + }, + }); + + if (result.count === 0) return null; // guard failed + + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Atomically claims a tx hash for one accepted intent. The unique index on + * intents.tx_hash arbitrates cross-intent races; the row predicate arbitrates + * concurrent attempts to replace a hash on the same intent. + */ + async reserveFillTxHash(id: string, solver: string, txHash: string): Promise { + const result = await this.prisma.intent.updateMany({ + where: { + intentId: id, + state: PrismaIntentState.accepted, + solver, + OR: [{ txHash: null }, { txHash }], + }, + data: { + txHash, + fillVerificationState: "pending", + fillVerificationReason: null, + }, + }); + if (result.count === 0) return null; + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Atomically cancel an intent only when it is currently `open`. Guards + * against a concurrent solver accept() or sweeper expiry on the same intent. + */ + async cancelIfOpen(id: string): Promise { + const result = await this.prisma.intent.updateMany({ + where: { intentId: id, state: PrismaIntentState.open }, + data: { state: PrismaIntentState.cancelled }, + }); + + if (result.count === 0) return null; + + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Atomically expire an intent only when it is currently `open`. Used by the + * sweeper so a concurrent user cancel() or solver accept() always wins the race. + */ + async expireIfOpen(id: string): Promise { + const result = await this.prisma.intent.updateMany({ + where: { intentId: id, state: PrismaIntentState.open }, + data: { state: PrismaIntentState.expired }, + }); + + if (result.count === 0) return null; + + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Atomically slash an intent only when it is currently `accepted`. Used by + * the sweeper so a concurrent solver fill() always wins the race. + */ + async slashIfAccepted( + id: string, + patch: { slashedAt: number; slashReason: string }, + ): Promise { + const result = await this.prisma.intent.updateMany({ + where: { intentId: id, state: PrismaIntentState.accepted }, + data: { state: PrismaIntentState.slashed }, + }); + + if (result.count === 0) return null; + + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + /** + * Issue #477 — push an accepted intent's deadline out during a fill pause. + * The `deadline < newDeadline` predicate makes this a no-op once the window + * is already long enough, so repeated sweep cycles cannot creep the deadline + * forward indefinitely. + */ + async extendDeadlineIfAccepted(id: string, newDeadline: number): Promise { + const result = await this.prisma.intent.updateMany({ + where: { + intentId: id, + state: PrismaIntentState.accepted, + deadline: { lt: newDeadline }, + }, + data: { deadline: newDeadline }, + }); + + if (result.count === 0) return null; + + const row = await this.prisma.intent.findUnique({ where: { intentId: id } }); + return row ? this.fromRow(row) : null; + } + + // ── Private helpers ──────────────────────────────────────────────────────── + + /** Map Intent → Prisma create/update data (omits intentId which is the key). */ + private toDbData( + intent: Intent, + ): Omit { + const data: Omit & { feeAmount?: string | null } = { + user: intent.user, + srcChain: intent.srcChain as Prisma.IntentCreateInput["srcChain"], + srcToken: intent.srcToken as unknown as Prisma.InputJsonValue, + srcAmount: intent.srcAmount, + dstToken: intent.dstToken as unknown as Prisma.InputJsonValue, + minDstAmount: intent.minDstAmount, + ...(intent.auction ? { auction: intent.auction as unknown as Prisma.InputJsonValue } : {}), + acceptedDstAmount: intent.acceptedDstAmount ?? null, + quotedDstAmount: intent.quotedDstAmount ?? null, + solver: intent.solver ?? null, + state: this.toPrismaState(intent.state), + createdAt: intent.createdAt, + deadline: intent.deadline, + filledAt: intent.filledAt ?? null, + fillAmount: intent.fillAmount ?? null, + txHash: intent.txHash ?? null, + ...(intent.usdValueAtCreate !== undefined + ? { usdValueAtCreate: intent.usdValueAtCreate } + : {}), + fillVerificationState: intent.fillVerificationState ?? null, + fillVerificationReason: intent.fillVerificationReason ?? null, + fillVerifiedAt: intent.fillVerifiedAt ? new Date(intent.fillVerifiedAt) : null, + }; + + if (intent.feeAmount !== undefined) { + (data as { feeAmount?: string | null }).feeAmount = intent.feeAmount ?? null; + } + + return data; + } + + /** Build an `updateMany`-compatible data object from a partial Intent patch. */ + private toDbPatch(patch: Partial): Prisma.IntentUpdateInput { + const data = {} as Prisma.IntentUpdateInput & { feeAmount?: string | null }; + if (patch.state !== undefined) data.state = this.toPrismaState(patch.state); + if (patch.solver !== undefined) data.solver = patch.solver; + if (patch.deadline !== undefined) data.deadline = patch.deadline; + if (patch.filledAt !== undefined) data.filledAt = patch.filledAt; + if (patch.fillAmount !== undefined) data.fillAmount = patch.fillAmount; + if (patch.feeAmount !== undefined) (data as { feeAmount?: string | null }).feeAmount = patch.feeAmount ?? null; + if (patch.txHash !== undefined) data.txHash = patch.txHash; + if (patch.fillVerificationState !== undefined) data.fillVerificationState = patch.fillVerificationState; + if (patch.fillVerificationReason !== undefined) data.fillVerificationReason = patch.fillVerificationReason; + if (patch.fillVerifiedAt !== undefined) data.fillVerifiedAt = patch.fillVerifiedAt ? new Date(patch.fillVerifiedAt) : null; + if (patch.quotedDstAmount !== undefined) data.quotedDstAmount = patch.quotedDstAmount; + if (patch.srcAmount !== undefined) data.srcAmount = patch.srcAmount; + if (patch.minDstAmount !== undefined) data.minDstAmount = patch.minDstAmount; + if (patch.usdValueAtCreate !== undefined) data.usdValueAtCreate = patch.usdValueAtCreate; + if (patch.auction !== undefined) { + (data as Prisma.IntentUpdateInput & { auction?: Prisma.InputJsonValue }).auction = + patch.auction as unknown as Prisma.InputJsonValue; + } + if (patch.acceptedDstAmount !== undefined) { + (data as Prisma.IntentUpdateInput & { acceptedDstAmount?: string | null }).acceptedDstAmount = + patch.acceptedDstAmount ?? null; + } + if ("slashedAt" in patch && patch.slashedAt !== undefined) { + // slashedAt / slashReason are not Prisma schema columns yet; ignore silently + // until the schema migration lands (issue #62). + } + return data; + } + + /** Map a Prisma Intent row → domain Intent. */ + private fromRow(row: { + intentId: string; + user: string; + srcChain: string; + srcToken: Prisma.JsonValue; + srcAmount: string; + dstToken: Prisma.JsonValue; + minDstAmount: string; + auction: Prisma.JsonValue | null; + acceptedDstAmount: string | null; + quotedDstAmount: string | null; + solver: string | null; + state: PrismaIntentState; + createdAt: number; + deadline: number; + filledAt: number | null; + fillAmount: string | null; + feeAmount?: string | null; + txHash: string | null; + usdValueAtCreate?: number | null; + fillVerificationState?: "pending" | "verified" | "rejected" | null; + fillVerificationReason?: string | null; + fillVerifiedAt?: Date | null; + }): Intent { + return { + intentId: row.intentId, + user: row.user, + srcChain: row.srcChain as Intent["srcChain"], + srcToken: row.srcToken as unknown as TokenInfo, + srcAmount: row.srcAmount, + dstToken: row.dstToken as unknown as StellarToken, + minDstAmount: row.minDstAmount, + ...(row.auction !== null ? { auction: row.auction as unknown as Intent["auction"] } : {}), + ...(row.acceptedDstAmount !== null ? { acceptedDstAmount: row.acceptedDstAmount } : {}), + ...(row.quotedDstAmount !== null ? { quotedDstAmount: row.quotedDstAmount } : {}), + ...(row.solver !== null ? { solver: row.solver } : {}), + state: row.state as IntentState, + createdAt: row.createdAt, + deadline: row.deadline, + ...(row.filledAt !== null ? { filledAt: row.filledAt } : {}), + ...(row.fillAmount !== null ? { fillAmount: row.fillAmount } : {}), + ...(row.feeAmount !== undefined && row.feeAmount !== null ? { feeAmount: row.feeAmount } : {}), + ...(row.txHash !== null ? { txHash: row.txHash } : {}), + ...(row.usdValueAtCreate !== null && row.usdValueAtCreate !== undefined + ? { usdValueAtCreate: row.usdValueAtCreate } + : {}), + ...(row.fillVerificationState ? { fillVerificationState: row.fillVerificationState } : {}), + ...(row.fillVerificationReason ? { fillVerificationReason: row.fillVerificationReason } : {}), + ...(row.fillVerifiedAt ? { fillVerifiedAt: row.fillVerifiedAt.toISOString() } : {}), + }; + } + + private toPrismaState(state: IntentState): PrismaIntentState { + return state as PrismaIntentState; + } +} diff --git a/src/intents/solver-intent-matcher.ts b/src/intents/solver-intent-matcher.ts index e69de29b..4dc7743a 100644 --- a/src/intents/solver-intent-matcher.ts +++ b/src/intents/solver-intent-matcher.ts @@ -0,0 +1,164 @@ +/** + * Solver-intent capability matcher (issue #436) + * ─────────────────────────────────────────────── + * Provides a fast per-solver predicate that decides whether an open intent + * is eligible for a given solver based on: + * • supported source chains + * • supported source tokens (by symbol) + * • remaining bond capacity (> 0) + * + * The predicate is pre-compiled once per solver connection/update and is + * applied synchronously during WS fan-out to keep CPU overhead low even + * at large subscriber counts. + * + * An intent index keyed by (srcChain, srcToken.symbol) is maintained so we + * can go from "solver connected" → "eligible intents" in O(supported-chains × + * supported-tokens) instead of O(all-open-intents). The same index is used + * by GET /solvers/:address/eligible-intents (see solvers.controller.ts) so + * both surfaces stay in sync automatically. + */ + +import { Injectable } from "@nestjs/common"; +import { Intent, SupportedChain } from "./intents.types"; +import { SolverRecord } from "../solvers/solvers.types"; +import { IntentsService } from "./intents.service"; +import { logger } from "../common/logger"; + +export interface SolverMatchPredicate { + /** Returns true iff an open intent is eligible for this solver. */ + matches(intent: Intent): boolean; + /** The solver address this predicate was compiled for. */ + solverAddress: string; + /** Snapshot of the solver's capabilities at compile time. */ + supportedChains: SupportedChain[]; + supportedTokens: string[]; + bondAmount: string; +} + +/** + * Builds a match predicate for a solver. The predicate is a plain closure so + * it is cheap to evaluate (no object allocations per intent check). + */ +export function buildMatchPredicate(solver: SolverRecord): SolverMatchPredicate { + // A missing capability list means the solver declared nothing, so it matches + // nothing. Defaulting to an empty set (rather than trusting the field to be + // present) keeps a partially-populated solver record from widening its own + // feed and keeps this hot path from throwing mid-broadcast. + const supportedChains = Array.isArray(solver.supportedChains) ? solver.supportedChains : []; + const supportedTokens = Array.isArray(solver.supportedTokens) ? solver.supportedTokens : []; + const chainSet = new Set(supportedChains); + const tokenSet = new Set(supportedTokens.map((t) => t.toLowerCase())); + // A missing / unparseable bond is treated as "no bond", so the solver matches + // nothing rather than throwing (or matching) on malformed data. + let hasBond = false; + try { + hasBond = BigInt(solver.bondAmount) > 0n; + } catch { + hasBond = false; + } + + return { + solverAddress: solver.address, + supportedChains: [...supportedChains], + supportedTokens: [...supportedTokens], + bondAmount: solver.bondAmount, + matches(intent: Intent): boolean { + if (!hasBond) return false; + if (!chainSet.has(intent.srcChain)) return false; + const symbol = + typeof intent.srcToken === "object" && intent.srcToken !== null + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + ((intent.srcToken as any).symbol as string | undefined) + : undefined; + return symbol ? tokenSet.has(symbol.toLowerCase()) : false; + }, + }; +} + +/** + * Intent index keyed by `${srcChain}:${srcTokenSymbol.toLowerCase()}`. + * + * Maintained by IntentCapabilityIndex so that: + * • Fan-out can skip intents that the solver definitely cannot fill. + * • GET /solvers/:address/eligible-intents returns O(1) candidates without + * scanning all open intents. + */ +@Injectable() +export class IntentCapabilityIndex { + // chain:token → Set of open intentIds + private readonly index = new Map>(); + // intentId → Intent (secondary lookup) + private readonly byId = new Map(); + + constructor(private readonly intentsService: IntentsService) {} + + private static key(chain: string, tokenSymbol: string): string { + return `${chain}:${tokenSymbol.toLowerCase()}`; + } + + /** + * Rebuild the full index from scratch from current open intents. + * Called on module init and after bulk state changes. + */ + async rebuild(): Promise { + this.index.clear(); + this.byId.clear(); + const open = await this.intentsService.getByState("open"); + for (const intent of open) { + this.addIntent(intent); + } + logger.debug( + `[intent-index] rebuilt: ${open.length} open intents, ${this.index.size} bucket(s)`, + ); + } + + /** Add (or refresh) a single intent in the index. */ + addIntent(intent: Intent): void { + const symbol = this.getSymbol(intent); + if (!symbol) return; + const k = IntentCapabilityIndex.key(intent.srcChain, symbol); + if (!this.index.has(k)) this.index.set(k, new Set()); + this.index.get(k)!.add(intent.intentId); + this.byId.set(intent.intentId, intent); + } + + /** Remove an intent from the index (call when it leaves the open state). */ + removeIntent(intentId: string): void { + const intent = this.byId.get(intentId); + if (!intent) return; + const symbol = this.getSymbol(intent); + if (symbol) { + const k = IntentCapabilityIndex.key(intent.srcChain, symbol); + this.index.get(k)?.delete(intentId); + } + this.byId.delete(intentId); + } + + /** + * Return all currently-indexed open intents that match the solver's + * capabilities. Used by GET /solvers/:address/eligible-intents and by + * the WS snapshot sent immediately after solver auth. + */ + getEligibleFor(solver: SolverRecord): Intent[] { + if (BigInt(solver.bondAmount) <= 0n) return []; + const result: Intent[] = []; + for (const chain of solver.supportedChains) { + for (const token of solver.supportedTokens) { + const k = IntentCapabilityIndex.key(chain, token); + const ids = this.index.get(k); + if (!ids) continue; + for (const id of ids) { + const intent = this.byId.get(id); + if (intent) result.push(intent); + } + } + } + return result; + } + + private getSymbol(intent: Intent): string | undefined { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const sym = (intent.srcToken as any)?.symbol; + return typeof sym === "string" && sym.length > 0 ? sym : undefined; + } +} diff --git a/src/intents/ws/connection-state.ts b/src/intents/ws/connection-state.ts index e69de29b..220e311a 100644 --- a/src/intents/ws/connection-state.ts +++ b/src/intents/ws/connection-state.ts @@ -0,0 +1,128 @@ +import type { WebSocket } from "ws"; + +/** + * Encoding format negotiated per WebSocket connection (Activity 1). + */ +export type EncodingFormat = "json" | "msgpack"; + +/** Classic token bucket: `ratePerSec` sustained, up to `burst` at once. */ +export class TokenBucket { + private tokens: number; + private last: number; + + constructor( + private readonly ratePerSec: number, + private readonly burst: number, + now = Date.now(), + ) { + this.tokens = burst; + this.last = now; + } + + /** Consumes one token; false when the bucket is empty. */ + take(now = Date.now()): boolean { + this.tokens = Math.min(this.burst, this.tokens + ((now - this.last) / 1000) * this.ratePerSec); + this.last = now; + if (this.tokens < 1) return false; + this.tokens -= 1; + return true; + } +} + +/** + * Client IP for per-IP limits. With `trustedHops = 0` the socket address is + * used and X-Forwarded-For is ignored (clients cannot spoof it). With N + * trusted proxies, the address N entries from the right of + * `X-Forwarded-For, remoteAddress` is the one the outermost trusted proxy saw. + */ +export function resolveClientIp( + remoteAddress: string | undefined, + forwardedFor: string | string[] | undefined, + trustedHops: number, +): string { + const socketIp = remoteAddress ?? "unknown"; + if (trustedHops <= 0 || !forwardedFor) return socketIp; + const header = Array.isArray(forwardedFor) ? forwardedFor.join(",") : forwardedFor; + const chain = [...header.split(",").map((s) => s.trim()).filter(Boolean), socketIp]; + return chain[Math.max(0, chain.length - 1 - trustedHops)]; +} + +export interface OutboundLimits { + queueMax: number; + bufferBytes: number; + policy: "drop_oldest" | "disconnect"; +} + +export type OutboundResult = "sent" | "queued" | "dropped_oldest" | "disconnected"; + +/** + * Per-connection state (issue #455): identity, inbound token bucket and a + * bounded outbound queue. + * + * Messages go straight to the socket while `bufferedAmount` is below + * `bufferBytes`; above it they wait in a queue of at most `queueMax` + * messages, flushed from the `send` callbacks as the socket drains. When the + * queue is full the policy either drops the oldest queued message or + * terminates the connection — so a slow consumer costs at most + * `bufferBytes + queueMax` messages of memory. + */ +export class ConnectionState { + readonly bucket: TokenBucket; + violations = 0; + /** Authenticated solver address (signature or JWT), if any. */ + identity: string | null = null; + /** Encoding format negotiated during handshake (Activity 1). */ + encoding: EncodingFormat = "json"; + private readonly queue: Array = []; + private closed = false; + + constructor( + private readonly socket: WebSocket, + readonly ip: string, + rate: { perSec: number; burst: number }, + private readonly limits: OutboundLimits, + ) { + this.bucket = new TokenBucket(rate.perSec, rate.burst); + } + + queued(): number { + return this.queue.length; + } + + send(payload: string | Uint8Array): OutboundResult { + if (this.closed || this.socket.readyState !== this.socket.OPEN) return "sent"; + if (this.queue.length === 0 && this.socket.bufferedAmount < this.limits.bufferBytes) { + this.write(payload); + return "sent"; + } + this.queue.push(payload); + if (this.queue.length <= this.limits.queueMax) return "queued"; + if (this.limits.policy === "disconnect") { + this.close(); + this.socket.terminate(); + return "disconnected"; + } + this.queue.shift(); + return "dropped_oldest"; + } + + close(): void { + this.closed = true; + this.queue.length = 0; + } + + private write(payload: string | Uint8Array) { + this.socket.send(payload, () => this.flush()); + } + + private flush() { + while ( + !this.closed && + this.queue.length > 0 && + this.socket.readyState === this.socket.OPEN && + this.socket.bufferedAmount < this.limits.bufferBytes + ) { + this.write(this.queue.shift()!); + } + } +} diff --git a/src/metrics/metrics.service.ts b/src/metrics/metrics.service.ts index e69de29b..fb8a5350 100644 --- a/src/metrics/metrics.service.ts +++ b/src/metrics/metrics.service.ts @@ -0,0 +1,548 @@ +import { Injectable, OnModuleInit } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import client from "prom-client"; +import { AppConfig } from "../config/configuration"; + +@Injectable() +export class MetricsService implements OnModuleInit { + private readonly register: client.Registry; + + // ── HTTP ─────────────────────────────────────────────────────────────────── + public readonly httpRequestDuration: client.Histogram; + public readonly httpRequestTotal: client.Counter; + public readonly httpRequestErrors: client.Counter; + + // ── Intent / WS general ─────────────────────────────────────────────────── + public readonly intentStateTransitions: client.Counter; + public readonly wsConnections: client.Gauge; + public readonly intentCreateDuration: client.Histogram; + public readonly wsDeliveryDuration: client.Histogram; + public readonly eventIngestionLag: client.Gauge; + + /** + * Shadow-mode divergence monitor (issue #401). + * + * `vortex_shadow_comparisons_total{transition,outcome}` counts every + * (expected, simulated) pair the monitor resolved, and + * `vortex_shadow_divergences_total{transition,reason}` counts the subset the + * classifier flagged. `vortex_shadow_dropped_total` and + * `vortex_shadow_queue_depth` expose monitor health so a starved monitor is + * never mistaken for a healthy one — the on-chain cutover runbook's go/no-go + * threshold is only meaningful while these are being exercised. + */ + public readonly shadowComparisons: client.Counter; + public readonly shadowDivergences: client.Counter; + public readonly shadowDropped: client.Counter; + public readonly shadowQueueDepth: client.Gauge; + + /** + * Leader election metrics (issue #493). + * Track which replica is leader per worker and how often leadership changes. + */ + public readonly leaderElectionIsLeader: client.Gauge; + public readonly leaderElectionChangesTotal: client.Counter; + + /** Background job metrics (issue #494). */ + public readonly jobsQueueDepth: client.Gauge; + public readonly jobsDuration: client.Histogram; + public readonly jobsFailures: client.Counter; + public readonly jobsDeadLettered: client.Counter; + private queueDepthProvider?: () => Promise>; + + /** Feature-flag evaluations (issue #495). */ + public readonly flagEvaluations: client.Counter; + // ── WS backplane (issue #454) ───────────────────────────────────────────── + public readonly wsBackplanePublishDuration: client.Histogram; + public readonly wsBackplaneDropped: client.Counter; + public readonly wsBackplaneConnected: client.Gauge; + + // ── WS hardening (issue #455) ───────────────────────────────────────────── + public readonly wsConnectionsRejected: client.Counter; + public readonly wsRateLimited: client.Counter; + public readonly wsOutboundDropped: client.Counter; + public readonly wsSlowConsumerDisconnects: client.Counter; + + // ── Health (issue #492) ─────────────────────────────────────────────────── + public readonly healthIndicatorUp: client.Gauge; + public readonly healthReady: client.Gauge; + public readonly healthCheckDuration: client.Histogram; + + /** + * Sweeper metrics — these replace the retired src/common/metrics.ts + * MetricsRegistry.sweeper namespace (see issue #259). + * + * The on-call runbook (docs/runbooks/on-call.md) references these names + * directly. Any change here must be reflected there. + */ + public readonly sweeperExpiredTotal: client.Counter; + public readonly sweeperSweepDurationMs: client.Histogram; + + // ── SLO SLIs (issue #480) ───────────────────────────────────────────────── + public readonly txConfirmationDuration: client.Histogram; + + // ── WS capability-filter metrics (issue #436) ──────────────────────────── + /** + * WS events delivered to an authenticated solver after capability filtering. + * Label `solver` is truncated to 12 chars to bound Prometheus label cardinality. + */ + public readonly wsEventsDeliveredTotal: client.Counter; + /** + * WS events suppressed by the capability filter (intent outside solver's + * supported chains/tokens or solver bond = 0). + */ + public readonly wsEventsFilteredTotal: client.Counter; + + // ── Restore-transaction metrics (issue #394) ───────────────────────────── + public readonly sorobanRestoreTotal: client.Counter; + public readonly sorobanRestoreFeeStroops: client.Histogram; + + // ── Remote signer call latency (issue #400) ─────────────────────────────── + public readonly signerCallDurationSeconds: client.Histogram; + + // ── Solver-registry event ingestion (issue #399) ────────────────────────── + public readonly solverRegistryEventsTotal: client.Counter; + public readonly legacyStellarSignatures: client.Counter; + + constructor(private readonly configService: ConfigService) { + this.register = new client.Registry(); + const prefix = "vortex_"; + + this.httpRequestDuration = new client.Histogram({ + name: `${prefix}http_request_duration_seconds`, + help: "HTTP request duration in seconds", + labelNames: ["method", "route", "status_code"], + buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], + registers: [this.register], + }); + + this.httpRequestTotal = new client.Counter({ + name: `${prefix}http_requests_total`, + help: "Total number of HTTP requests", + labelNames: ["method", "route", "status_code"], + registers: [this.register], + }); + + this.httpRequestErrors = new client.Counter({ + name: `${prefix}http_request_errors_total`, + help: "Total number of HTTP request errors (5xx)", + labelNames: ["method", "route", "status_code"], + registers: [this.register], + }); + + this.intentStateTransitions = new client.Counter({ + name: `${prefix}intent_state_transitions_total`, + help: "Total number of intent state transitions", + labelNames: ["from_state", "to_state"], + registers: [this.register], + }); + + this.wsConnections = new client.Gauge({ + name: `${prefix}ws_connections_active`, + help: "Number of active WebSocket connections", + registers: [this.register], + }); + + // ── Sweeper metrics (issue #259) ───────────────────────────────────────── + this.sweeperExpiredTotal = new client.Counter({ + name: `${prefix}sweeper_expired_total`, + help: "Total number of intents expired across all sweeps", + registers: [this.register], + }); + + this.sweeperSweepDurationMs = new client.Histogram({ + name: `${prefix}sweeper_sweep_duration_ms`, + help: "Duration of each IntentsSweeperService.sweep() execution in milliseconds", + buckets: [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000], + registers: [this.register], + }); + + // ── SLO SLIs (issue #480) ─────────────────────────────────────────────── + this.intentCreateDuration = new client.Histogram({ + name: `${prefix}intent_create_duration_seconds`, + help: "Intent-create handler latency in seconds", + labelNames: ["route"], + buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5], + registers: [this.register], + }); + + this.wsDeliveryDuration = new client.Histogram({ + name: `${prefix}ws_delivery_duration_seconds`, + help: "WS end-to-end delivery latency (broadcast to send) in seconds", + buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5], + registers: [this.register], + }); + + this.eventIngestionLag = new client.Gauge({ + name: `${prefix}event_ingestion_lag_seconds`, + help: "Event-ingestion lag: now minus newest ingested event timestamp", + registers: [this.register], + }); + + this.txConfirmationDuration = new client.Histogram({ + name: `${prefix}tx_confirmation_duration_seconds`, + help: "Fill submission to on-chain confirmation latency in seconds", + buckets: [1, 5, 15, 30, 60, 120, 300], + registers: [this.register], + }); + + // ── WS capability-filter metrics (issue #436) ────────────────────────── + this.wsEventsDeliveredTotal = new client.Counter({ + name: `${prefix}ws_events_delivered_total`, + help: "WS events delivered to authenticated solvers after capability filtering", + labelNames: ["solver"], + registers: [this.register], + }); + + this.wsEventsFilteredTotal = new client.Counter({ + name: `${prefix}ws_events_filtered_total`, + help: "WS events suppressed by capability filter (intent outside solver's chains/tokens)", + labelNames: ["solver"], + registers: [this.register], + }); + + // ── Restore-transaction metrics (issue #394) ─────────────────────────── + this.sorobanRestoreTotal = new client.Counter({ + name: `${prefix}soroban_restore_total`, + help: "Total RestoreFootprint transactions submitted", + labelNames: ["result"], + registers: [this.register], + }); + + this.sorobanRestoreFeeStroops = new client.Histogram({ + name: `${prefix}soroban_restore_fee_stroops`, + help: "Fee paid for RestoreFootprint transactions in stroops", + buckets: [1000, 5000, 10000, 50000, 100000, 500000, 1000000], + registers: [this.register], + }); + + // ── Remote signer latency (issue #400) ──────────────────────────────── + this.signerCallDurationSeconds = new client.Histogram({ + name: `${prefix}signer_call_duration_seconds`, + help: "Remote signer call latency in seconds", + labelNames: ["backend", "operation"], + buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5], + registers: [this.register], + }); + + // ── Solver-registry event ingestion (issue #399) ────────────────────── + this.solverRegistryEventsTotal = new client.Counter({ + name: `${prefix}solver_registry_events_total`, + help: "Solver-registry contract events ingested by type", + labelNames: ["event_type"], + registers: [this.register], + }); + + this.legacyStellarSignatures = new client.Counter({ + name: `${prefix}legacy_stellar_signatures_total`, + help: "Accepted version 1 Stellar intent signatures during the deprecation window", + labelNames: ["action"], + registers: [this.register], + }); + + // ── Shadow-mode divergence monitor (issue #401) ────────────────────────── + this.shadowComparisons = new client.Counter({ + name: `${prefix}shadow_comparisons_total`, + help: "Shadow-mode (expected, simulated) outcome pairs resolved, by transition, expected outcome and simulated outcome", + labelNames: ["transition", "expected", "outcome"], + registers: [this.register], + }); + + this.shadowDivergences = new client.Counter({ + name: `${prefix}shadow_divergences_total`, + help: "Shadow-mode divergences between the off-chain and simulated on-chain outcome, by transition and reason", + labelNames: ["transition", "reason"], + registers: [this.register], + }); + + this.shadowDropped = new client.Counter({ + name: `${prefix}shadow_dropped_total`, + help: "Shadow-mode observations dropped because the bounded queue was full", + registers: [this.register], + }); + + this.shadowQueueDepth = new client.Gauge({ + name: `${prefix}shadow_queue_depth`, + help: "Current number of queued shadow-mode observations awaiting simulation", + registers: [this.register], + }); + + // ── Leader election metrics (issue #493) ───────────────────────────────── + this.leaderElectionIsLeader = new client.Gauge({ + name: `${prefix}leader_election_is_leader`, + help: "1 when this replica is the current leader for the named worker, 0 otherwise", + labelNames: ["worker"], + registers: [this.register], + }); + + this.leaderElectionChangesTotal = new client.Counter({ + name: `${prefix}leader_election_changes_total`, + help: "Total number of leadership transitions (acquisitions + losses) per worker", + labelNames: ["worker", "transition"], + registers: [this.register], + }); + + // ── Background jobs (issue #494) ──────────────────────────────────────── + // Depth is sampled on scrape from the active queue driver, so it reflects + // every instance's shared view of the queue (BullMQ) without a timer. + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + this.jobsQueueDepth = new client.Gauge({ + name: `${prefix}jobs_queue_depth`, + help: "Jobs per queue and state (waiting, active, delayed, dead_letter)", + labelNames: ["queue", "state"], + registers: [this.register], + async collect() { + if (!self.queueDepthProvider) return; + this.reset(); + for (const { queue, state, count } of await self.queueDepthProvider()) { + this.set({ queue, state }, count); + } + }, + }); + + this.jobsDuration = new client.Histogram({ + name: `${prefix}jobs_duration_seconds`, + help: "Job handler latency in seconds", + labelNames: ["queue", "job", "outcome"], + buckets: [0.01, 0.05, 0.1, 0.5, 1, 5, 15, 60], + registers: [this.register], + }); + + this.jobsFailures = new client.Counter({ + name: `${prefix}jobs_failures_total`, + help: "Failed job attempts (including ones that will be retried)", + labelNames: ["queue", "job"], + registers: [this.register], + }); + + this.jobsDeadLettered = new client.Counter({ + name: `${prefix}jobs_dead_lettered_total`, + help: "Jobs moved to the dead-letter queue after exhausting retries", + labelNames: ["queue", "job"], + registers: [this.register], + }); + + // ── Feature flags (issue #495) ────────────────────────────────────────── + this.flagEvaluations = new client.Counter({ + name: `${prefix}flag_evaluations_total`, + help: "Feature-flag evaluations by flag, resolved value and reason", + labelNames: ["flag", "value", "reason"], + registers: [this.register], + }); + + // ── WS backplane (issue #454) ──────────────────────────────────────────── + this.wsBackplanePublishDuration = new client.Histogram({ + name: `${prefix}ws_backplane_publish_duration_seconds`, + help: "Time to sequence one WS event through the Redis backplane", + buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 1], + registers: [this.register], + }); + this.wsBackplaneDropped = new client.Counter({ + name: `${prefix}ws_backplane_dropped_total`, + help: "WS events dropped by the backplane, by reason", + labelNames: ["reason"], + registers: [this.register], + }); + this.wsBackplaneConnected = new client.Gauge({ + name: `${prefix}ws_backplane_connected`, + help: "1 while this replica is reading from the Redis backplane", + registers: [this.register], + }); + + // ── WS hardening (issue #455) ──────────────────────────────────────────── + this.wsConnectionsRejected = new client.Counter({ + name: `${prefix}ws_connections_rejected_total`, + help: "WS connections refused at admission, by reason (max_connections, per_ip)", + labelNames: ["reason"], + registers: [this.register], + }); + this.wsRateLimited = new client.Counter({ + name: `${prefix}ws_rate_limited_total`, + help: "Inbound WS messages rejected by the per-connection token bucket, by action", + labelNames: ["action"], + registers: [this.register], + }); + this.wsOutboundDropped = new client.Counter({ + name: `${prefix}ws_outbound_dropped_total`, + help: "Outbound WS messages dropped for slow consumers (drop-oldest policy)", + registers: [this.register], + }); + this.wsSlowConsumerDisconnects = new client.Counter({ + name: `${prefix}ws_slow_consumer_disconnects_total`, + help: "WS connections closed because their outbound queue exceeded its bound", + registers: [this.register], + }); + + // ── Health (issue #492) ────────────────────────────────────────────────── + this.healthIndicatorUp = new client.Gauge({ + name: `${prefix}health_indicator_up`, + help: "1 when the named health indicator's last check passed, else 0", + labelNames: ["indicator"], + registers: [this.register], + }); + this.healthReady = new client.Gauge({ + name: `${prefix}health_ready`, + help: "1 when this replica reports ready (after hysteresis), else 0", + registers: [this.register], + }); + this.healthCheckDuration = new client.Histogram({ + name: `${prefix}health_check_duration_seconds`, + help: "Duration of background health-indicator checks", + labelNames: ["indicator"], + buckets: [0.005, 0.01, 0.05, 0.1, 0.5, 1, 3], + registers: [this.register], + }); + } + + /** Registers the source sampled for `vortex_jobs_queue_depth` on each scrape. */ + setQueueDepthProvider( + provider: () => Promise>, + ): void { + this.queueDepthProvider = provider; + } + + onModuleInit() { + const prefix = "vortex_"; + client.collectDefaultMetrics({ register: this.register, prefix }); + } + + async metrics(): Promise { + return this.register.metrics(); + } + + contentType(): string { + return this.register.contentType; + } + + incIntentStateTransition(from: string, to: string) { + this.intentStateTransitions.inc({ from_state: from, to_state: to }); + } + + incWsConnection() { + this.wsConnections.inc(); + } + + decWsConnection() { + this.wsConnections.dec(); + } + + /** + * Record one sweeper cycle's expired count and duration. + * Called by IntentsSweeperService at the end of every sweep() invocation. + */ + recordSweep(expiredCount: number, durationMs: number): void { + this.sweeperExpiredTotal.inc(expiredCount); + this.sweeperSweepDurationMs.observe(durationMs); + } + + /** + * Observe intent-create latency (SLO SLI, issue #480). + * Call from the create path with handler duration in seconds. + */ + observeIntentCreate(durationSeconds: number, route = "POST /api/v1/intents"): void { + this.intentCreateDuration.observe({ route }, durationSeconds); + } + + /** + * Observe WS end-to-end delivery latency (SLO SLI, issue #480). + * Call from the gateway broadcast path with queue-to-send duration. + */ + observeWsDelivery(durationSeconds: number): void { + this.wsDeliveryDuration.observe(durationSeconds); + } + + /** Set current event-ingestion lag in seconds (SLO SLI, issue #480). */ + setIngestionLag(lagSeconds: number): void { + this.eventIngestionLag.set(lagSeconds); + } + + /** Observe fill-to-confirmation latency in seconds (SLO SLI, issue #480). */ + observeTxConfirmation(durationSeconds: number): void { + this.txConfirmationDuration.observe(durationSeconds); + } + + // ── WS capability-filter helpers (issue #436) ──────────────────────────── + + /** Record a WS event delivered to an authenticated solver (post-filter). */ + incWsDelivered(solverAddress: string): void { + this.wsEventsDeliveredTotal.inc({ solver: solverAddress.slice(0, 12) }); + } + + /** Record a WS event suppressed for a solver by the capability filter. */ + incWsFiltered(solverAddress: string): void { + this.wsEventsFilteredTotal.inc({ solver: solverAddress.slice(0, 12) }); + } + + // ── Restore-transaction helpers (issue #394) ────────────────────────────── + + incSorobanRestore(result: "success" | "failed"): void { + this.sorobanRestoreTotal.inc({ result }); + } + + observeRestoreFee(stroops: number): void { + this.sorobanRestoreFeeStroops.observe(stroops); + } + + // ── Remote signer helpers (issue #400) ──────────────────────────────────── + + observeSignerCall(backend: string, operation: string, durationSeconds: number): void { + this.signerCallDurationSeconds.observe({ backend, operation }, durationSeconds); + } + + // ── Solver-registry event ingestion helpers (issue #399) ───────────────── + + incSolverRegistryEvent(eventType: string): void { + this.solverRegistryEventsTotal.inc({ event_type: eventType }); + } + + /** + * Record one resolved shadow-mode comparison (issue #401). + * + * `expected` is the off-chain verdict and `outcome` the simulated one, so + * the pair required by the issue stays queryable from PromQL: + * `...{expected="ok",outcome="rejected"}` is the "contract would have + * refused a transition we committed" case, and the reverse label pair is the + * "we refused something the contract allows" case. Cardinality is bounded at + * 5 transitions x 2 expected x 4 outcomes. + * + * `outcome` is `"unavailable"` when the simulation never produced a verdict + * (unconfigured contract, RPC unreachable) so that case stays + * distinguishable in PromQL from a contract that actively said no. + */ + recordShadowComparison(transition: string, expected: string, outcome: string): void { + this.shadowComparisons.inc({ transition, expected, outcome }); + } + + /** Record one classified shadow-mode divergence (issue #401). */ + recordShadowDivergence(transition: string, reason: string): void { + this.shadowDivergences.inc({ transition, reason }); + } + + /** Record one shadow-mode observation dropped by the bounded queue. */ + recordShadowDrop(): void { + this.shadowDropped.inc(); + } + + /** Publish the current shadow queue depth. */ + setShadowQueueDepth(depth: number): void { + this.shadowQueueDepth.set(depth); + } + + /** + * Record that this replica acquired leadership for `workerName`. + * Sets the is_leader gauge to 1 and increments the acquisition counter. + */ + recordLeadershipAcquired(workerName: string): void { + this.leaderElectionIsLeader.set({ worker: workerName }, 1); + this.leaderElectionChangesTotal.inc({ worker: workerName, transition: "acquired" }); + } + + /** + * Record that this replica lost leadership for `workerName`. + * Sets the is_leader gauge to 0 and increments the lost counter. + */ + recordLeadershipLost(workerName: string): void { + this.leaderElectionIsLeader.set({ worker: workerName }, 0); + this.leaderElectionChangesTotal.inc({ worker: workerName, transition: "lost" }); + } +} diff --git a/src/solvers/solvers.controller.ts b/src/solvers/solvers.controller.ts index e69de29b..5e09dfcf 100644 --- a/src/solvers/solvers.controller.ts +++ b/src/solvers/solvers.controller.ts @@ -0,0 +1,395 @@ +import { + BadRequestException, + Body, + Controller, + ForbiddenException, + Get, + NotFoundException, + Param, + Patch, + Post, + Query, +} from "@nestjs/common"; +import { + ApiNotFoundResponse, + ApiOperation, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; +import { ConfigService } from "@nestjs/config"; +import { IntentsService } from "../intents/intents.service"; +import { IntentCapabilityIndex } from "../intents/solver-intent-matcher"; +import { SUPPORTED_CHAINS, SupportedChain } from "../intents/intents.types"; +import { ListIntentsDto } from "../intents/dto/list-intents.dto"; +import { + buildDisputeMessage, + buildRegisterMessage, + buildSolverStatusMessage, + buildUpdateSolverMessage, + verifyStellarSignature, +} from "../common/stellar-signature"; +import { isCanaryIntent } from "../common/canary"; +import { AppConfig } from "../config/configuration"; +import { SolversService, LeaderboardWindow } from "./solvers.service"; +import { ListIntentsDto } from "../intents/dto/list-intents.dto"; +import { AppConfig } from "../config/configuration"; +import { isCanaryIntent } from "../common/canary"; +import { IntentCapabilityIndex } from "../intents/solver-intent-matcher"; +import { RegisterSolverDto } from "./dto/register-solver.dto"; +import { UpdateSolverDto } from "./dto/update-solver.dto"; +import { UpdateSolverStatusDto } from "./dto/update-solver-status.dto"; +import { SolverCredentialService } from "../auth/solver-credentials/solver-credential.service"; + +const WINDOW_SECONDS: Record, number> = { + "24h": 24 * 60 * 60, + "7d": 7 * 24 * 60 * 60, + "30d": 30 * 24 * 60 * 60, +}; + +@ApiTags("solvers") +@Controller("api/v1/solvers") +export class SolversController { + constructor( + private readonly solversService: SolversService, + private readonly intentsService: IntentsService, + private readonly intentIndex: IntentCapabilityIndex, + private readonly credentialService: SolverCredentialService, + config: ConfigService, + ) { + this.canary = new Set(config.get("canaryAddresses", { infer: true }) ?? []); + } + + /** Canary addresses (issue #496) — excluded from every leaderboard. */ + private readonly canary: ReadonlySet; + + @Post() + async register(@Body() dto: RegisterSolverDto) { + verifyStellarSignature(dto.address, buildRegisterMessage(dto.address), dto.proofSignature); + + const onchainEnabled = (process.env.ONCHAIN_INTENTS_ENABLED ?? "false") === "true"; + + if (onchainEnabled) { + // Issue #399: when on-chain intents are enabled, POST /solvers is a + // metadata-only endpoint. Bond amount is authoritative on-chain; the + // REST endpoint may not set it. Supported chains/tokens and name are + // still accepted and merged into any existing record. + const existing = await this.solversService.get(dto.address); + if (existing) { + // Update metadata fields only — bond unchanged. + return this.solversService.register({ + address: dto.address, + name: dto.name, + bondAmount: existing.bondAmount, // preserve on-chain bond + avgFillTime: dto.avgFillTime, + isActive: existing.isActive, + supportedChains: dto.supportedChains, + supportedTokens: dto.supportedTokens, + }); + } + // First-time metadata registration (bond will be set by on-chain event). + return this.solversService.register({ + address: dto.address, + name: dto.name, + bondAmount: "0", // bond is always set by chain events when ONCHAIN_INTENTS_ENABLED + avgFillTime: dto.avgFillTime, + isActive: true, + supportedChains: dto.supportedChains, + supportedTokens: dto.supportedTokens, + }); + } + + return this.solversService.register({ + address: dto.address, + name: dto.name, + bondAmount: dto.bondAmount, + avgFillTime: dto.avgFillTime, + isActive: true, + supportedChains: dto.supportedChains, + supportedTokens: dto.supportedTokens, + }); + } + + @Get("leaderboard") + @ApiOperation({ + summary: "Windowed solver leaderboard", + description: + "Returns the ranked solver list for a specific window. This endpoint is intended for recent-performance visibility and does not alter the legacy all-time leaderboard.", + }) + @ApiQuery({ name: "window", required: false, enum: ["24h", "7d", "30d", "all"], description: "Time window over which to compute rankings." }) + async getLeaderboard(@Query("window") window: string = "all") { + const resolvedWindow = this.normalizeWindow(window); + const solvers = (await this.solversService.getAll()).filter((s) => !this.canary.has(s.address)); + const intents = (await this.intentsService.getAll()).filter((i) => !isCanaryIntent(i, this.canary)); + const now = Math.floor(Date.now() / 1000); + const cutoff = resolvedWindow === "all" ? 0 : now - WINDOW_SECONDS[resolvedWindow]; + + const ranked = solvers + .map((solver) => { + const recentIntents = intents.filter((intent) => { + if (intent.solver !== solver.address || intent.state !== "filled") return false; + const timestamp = intent.filledAt ?? intent.createdAt; + return resolvedWindow === "all" || timestamp >= cutoff; + }); + + const slashedRecent = intents.filter((intent) => { + if (intent.solver !== solver.address || intent.state !== "slashed") return false; + const timestamp = intent.slashedAt ?? intent.createdAt; + return resolvedWindow === "all" || timestamp >= cutoff; + }); + + const fillsCompleted = recentIntents.length; + const fillsFailed = slashedRecent.length; + const total = fillsCompleted + fillsFailed; + const successRate = total > 0 ? fillsCompleted / total : 0; + const ageDays = Math.max(0, (now - solver.registeredAt) / 86400); + const reputationScore = Number( + (successRate * Math.exp(-ageDays / 180)).toFixed(4), + ); + + return { + address: solver.address, + name: solver.name, + fillsCompleted, + fillsFailed, + successRate: Number(successRate.toFixed(4)), + reputationScore, + totalVolume: recentIntents + .reduce((sum, intent) => sum + BigInt(intent.fillAmount ?? "0"), 0n) + .toString(), + avgFillTime: recentIntents.length + ? Math.round( + recentIntents.reduce((sum, intent) => { + if (!intent.filledAt) return sum; + return sum + (intent.filledAt - intent.createdAt); + }, 0) / recentIntents.length, + ) + : 0, + bondAmount: solver.bondAmount, + isActive: solver.isActive, + window: resolvedWindow, + }; + }) + .filter((entry) => entry.fillsCompleted > 0 || entry.fillsFailed > 0 || resolvedWindow === "all") + .sort((a, b) => b.reputationScore - a.reputationScore || b.fillsCompleted - a.fillsCompleted); + + return { solvers: ranked, count: ranked.length, window: resolvedWindow }; + } + + @Get() + async getLegacyLeaderboard() { + const solvers = (await this.solversService.getAll()) + .filter((s) => !this.canary.has(s.address)) + .sort((a, b) => b.fillsCompleted - a.fillsCompleted); + return { solvers, count: solvers.length }; + } + + @Get(":address/eligible-intents") + async getEligibleIntents(@Param("address") address: string, @Query() dto: ListIntentsDto) { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + if (!solver.isActive) throw new ForbiddenException("Solver is not active"); + + // Use the capability index for O(supported-chains × supported-tokens) + // lookup instead of scanning all open intents (issue #436). + const eligible = this.intentIndex.getEligibleFor(solver); + + const limit = Math.min(dto.limit ?? 20, 100); + const offset = dto.offset ?? 0; + + if ((dto.limit ?? 20) > 100) { + throw new BadRequestException("Limit exceeds maximum allowed value of 100"); + } + + const page = eligible.slice(offset, offset + limit); + return { intents: page, total: eligible.length, count: eligible.length, limit, offset }; + } + + @Get(":address") + + async getSolver(@Param("address") address: string) { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + return solver; + } + + @Get(":address/stats") + async getSolverStats(@Param("address") address: string, @Query("window") window?: string) { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + + const resolvedWindow = this.normalizeWindow(window ?? "all"); + const intents = await this.intentsService.getAll(); + const now = Math.floor(Date.now() / 1000); + const cutoff = resolvedWindow === "all" ? 0 : now - WINDOW_SECONDS[resolvedWindow]; + + const recentIntents = intents.filter((intent) => { + if (intent.solver !== address) return false; + const timestamp = intent.state === "filled" ? intent.filledAt ?? intent.createdAt : intent.slashedAt ?? intent.createdAt; + return resolvedWindow === "all" || timestamp >= cutoff; + }); + + const fillsCompleted = recentIntents.filter((intent) => intent.state === "filled").length; + const fillsFailed = recentIntents.filter((intent) => intent.state === "slashed").length; + const total = fillsCompleted + fillsFailed; + const successRate = total > 0 ? fillsCompleted / total : 0; + const ageDays = Math.max(0, (now - solver.registeredAt) / 86400); + const reputationScore = Number((successRate * Math.exp(-ageDays / 180)).toFixed(4)); + + return { + address: solver.address, + name: solver.name, + fillsCompleted, + fillsFailed, + successRate: Number(successRate.toFixed(4)), + reputationScore, + totalVolume: recentIntents + .filter((intent) => intent.state === "filled") + .reduce((sum, intent) => sum + BigInt(intent.fillAmount ?? "0"), 0n) + .toString(), + avgFillTime: recentIntents.filter((intent) => intent.state === "filled" && intent.filledAt != null).length + ? Math.round( + recentIntents + .filter((intent) => intent.state === "filled" && intent.filledAt != null) + .reduce((sum, intent) => sum + (intent.filledAt! - intent.createdAt), 0) / + recentIntents.filter((intent) => intent.state === "filled" && intent.filledAt != null).length, + ) + : 0, + bondAmount: solver.bondAmount, + window: resolvedWindow, + }; + } + + @Get(":address/slashes") + async getSlashHistory(@Param("address") address: string, @Query("page") page = "1", @Query("pageSize") pageSize = "25") { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + + const pageNumber = Number(page) || 1; + const pageSizeNumber = Number(pageSize) || 25; + return this.solversService.getSlashHistory(address, pageNumber, pageSizeNumber); + } + + @Post(":address/slashes/:slashId/dispute") + async submitDispute( + @Param("address") address: string, + @Param("slashId") slashId: string, + @Body() dto: { reason: string; evidenceReference?: string; signature: string }, + ) { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + + verifyStellarSignature( + address, + buildDisputeMessage(slashId, address, dto.reason), + dto.signature, + ); + + const record = await this.solversService.submitDispute( + address, + slashId, + dto.reason, + dto.evidenceReference, + ); + if (!record) throw new NotFoundException("Slash record not found"); + return record; + } + + @Post(":address/slashes/:slashId/dispute/resolve") + async resolveDispute( + @Param("address") address: string, + @Param("slashId") slashId: string, + @Body() dto: { resolution: "resolved-upheld" | "resolved-reversed"; reviewer?: string; note?: string }, + ) { + const solver = await this.solversService.get(address); + if (!solver) throw new NotFoundException("Solver not found"); + + const record = await this.solversService.resolveDispute( + address, + slashId, + dto.resolution, + dto.reviewer, + dto.note, + ); + if (!record) throw new NotFoundException("Slash record not found"); + return record; + } + + @Post(":address/deregister") + async deregisterSolver(@Param("address") address: string, @Body() dto: UpdateSolverStatusDto) { + verifyStellarSignature(address, buildSolverStatusMessage("deregister", address), dto.signature); + + const solver = await this.solversService.deregister(address); + if (!solver) throw new NotFoundException("Solver not found"); + // Issue #443 — instantly disable every credential of the deregistered solver. + await this.credentialService.disableAllForSolver(address); + return { + ...solver, + withdrawalStatus: "pending", + withdrawalRequestedAt: Math.floor(Date.now() / 1000), + }; + } + + @Post(":address/deactivate") + async deactivate(@Param("address") address: string, @Body() dto: UpdateSolverStatusDto) { + verifyStellarSignature(address, buildSolverStatusMessage("deactivate", address), dto.signature); + + const solver = await this.solversService.deactivate(address); + if (!solver) throw new NotFoundException("Solver not found"); + // Issue #443 — instantly disable every credential of the deactivated solver. + await this.credentialService.disableAllForSolver(address); + return solver; + } + + @Post(":address/reactivate") + async reactivate(@Param("address") address: string, @Body() dto: UpdateSolverStatusDto) { + verifyStellarSignature(address, buildSolverStatusMessage("reactivate", address), dto.signature); + const solver = await this.solversService.reactivate(address); + if (!solver) throw new NotFoundException("Solver not found"); + return solver; + } + + /** + * PATCH /api/v1/solvers/:address — issue #273. + * + * Partial update of the solver's *mutable* profile fields. Requires an + * Ed25519 signature over `update-solver:
` from the solver's own + * key, so a third party cannot rewrite another solver's listing. + * + * Immutable fields (bond, fill counters, volume, registeredAt, isActive) are + * not present on `UpdateSolverDto`, so the global + * `ValidationPipe({ whitelist: true })` strips them from the body before the + * handler runs — they are silently ignored rather than rejected. + */ + @Patch(":address") + @ApiOperation({ + summary: "Update a solver's mutable profile fields", + description: + "Partial update of name, supportedChains, supportedTokens and avgFillTime. " + + "Requires an Ed25519 signature over the message `update-solver:
` " + + "produced by the solver's own key. Array fields are replaced wholesale. " + + "Immutable fields are silently ignored.", + }) + @ApiNotFoundResponse({ description: "Solver not found" }) + async update(@Param("address") address: string, @Body() dto: UpdateSolverDto) { + verifyStellarSignature(address, buildUpdateSolverMessage(address), dto.signature); + + const updated = await this.solversService.update(address, { + name: dto.name, + avgFillTime: dto.avgFillTime, + supportedChains: dto.supportedChains, + supportedTokens: dto.supportedTokens, + }); + + if (!updated) throw new NotFoundException("Solver not found"); + return updated; + } + + + private normalizeWindow(window?: string): LeaderboardWindow { + const normalized = (window ?? "all").toLowerCase(); + if (normalized === "all" || normalized === "24h" || normalized === "7d" || normalized === "30d") { + return normalized as LeaderboardWindow; + } + throw new BadRequestException("Unsupported leaderboard window. Choose 24h, 7d, 30d, or all."); + } +} diff --git a/src/soroban/contracts/settlement.client.ts b/src/soroban/contracts/settlement.client.ts index e69de29b..e10ae63e 100644 --- a/src/soroban/contracts/settlement.client.ts +++ b/src/soroban/contracts/settlement.client.ts @@ -0,0 +1,230 @@ +/** + * Read-only client for the on-chain settlement contract. + * + * Uses `simulateTransaction` to call view functions (get_intent, get_state) + * without ever submitting a transaction. This is safe at any time — simulation + * never mutates ledger state. + * + * Used by ReconcilerService to read the canonical on-chain intent state and + * diff it against Postgres. + * + * @module soroban/contracts/settlement.client + */ + +import { Injectable, Logger } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { + Address, + BASE_FEE, + Contract, + Keypair, + Networks, + SorobanRpc, + TransactionBuilder, + nativeToScVal, + scValToNative, + xdr, +} from "@stellar/stellar-sdk"; +import type { AppConfig } from "../../config/configuration"; + +// ─── Types ──────────────────────────────────────────────────────────────────── + +export type OnChainIntentState = + | "open" + | "accepted" + | "filled" + | "cancelled" + | "expired" + | "slashed" + | "unknown"; + +export interface OnChainIntent { + intentId: string; + user: string; + solver: string | null; + state: OnChainIntentState; + srcAmount: bigint; + minDstAmount: bigint; + fillAmount: bigint | null; + deadline: bigint; +} + +export interface SimulationResult { + ok: true; + value: T; + /** Raw simulation result for debugging. */ + rawReturnValue: xdr.ScVal; +} + +export type SimulationError = { + ok: false; + error: string; + /** "not_found" when the contract returns a None/null (intent doesn't exist on-chain). */ + reason: "not_found" | "simulation_error" | "decode_error" | "not_configured"; +}; + +const NETWORK_PASSPHRASE: Record = { + testnet: Networks.TESTNET, + futurenet: Networks.FUTURENET, + mainnet: Networks.PUBLIC, +}; + +/** + * Maps the raw contract state string/symbol to our canonical `OnChainIntentState`. + */ +function mapContractState(raw: unknown): OnChainIntentState { + const s = String(raw ?? "").toLowerCase(); + switch (s) { + case "open": return "open"; + case "accepted": return "accepted"; + case "filled": return "filled"; + case "cancelled": return "cancelled"; + case "expired": return "expired"; + case "slashed": return "slashed"; + default: return "unknown"; + } +} + +@Injectable() +export class SettlementClient { + private readonly logger = new Logger(SettlementClient.name); + private readonly server: SorobanRpc.Server; + private readonly contractId: string; + private readonly networkPassphrase: string; + + constructor(configService: ConfigService) { + const rpcUrl = configService.get("stellar.sorobanRpcUrl", { infer: true }); + this.server = new SorobanRpc.Server(rpcUrl, { + allowHttp: rpcUrl.startsWith("http://"), + }); + this.contractId = configService.get("stellar.settlementContractId", { infer: true }); + const network = configService.get("stellar.network", { infer: true }); + this.networkPassphrase = NETWORK_PASSPHRASE[network]; + } + + /** True when a settlement contract ID is configured. */ + get isConfigured(): boolean { + return this.contractId.length > 0; + } + + /** + * Read the intent record from the settlement contract for `intentId`. + * + * Uses a simulation against a throw-away ephemeral account so no real + * account with a sequence number is needed for read-only calls. + * + * Returns `SimulationError` with `reason: "not_found"` when the contract + * returns None (the intent does not exist on-chain). + */ + async getIntent( + intentId: string, + ): Promise | SimulationError> { + if (!this.isConfigured) { + return { ok: false, error: "SETTLEMENT_CONTRACT_ID not configured", reason: "not_configured" }; + } + + try { + const ephemeralKeypair = Keypair.random(); + const account = await this.server.getAccount(ephemeralKeypair.publicKey()).catch(() => { + // Build a synthetic account-like object with sequence 0 for simulation. + // simulateTransaction does not validate the account exists on ledger. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return { + id: ephemeralKeypair.publicKey(), + sequence: "0", + accountId() { return ephemeralKeypair.publicKey(); }, + sequenceNumber() { return "0"; }, + incrementSequenceNumber() { return; }, + } as any; // eslint-disable-line @typescript-eslint/no-explicit-any + }); + + const contract = new Contract(this.contractId); + const operation = contract.call( + "get_intent", + nativeToScVal(intentId, { type: "string" }), + ); + + const tx = new TransactionBuilder(account, { + fee: BASE_FEE, + networkPassphrase: this.networkPassphrase, + }) + .addOperation(operation) + .setTimeout(30) + .build(); + + const simulation = await this.server.simulateTransaction(tx); + + if (SorobanRpc.Api.isSimulationError(simulation)) { + const errStr = simulation.error; + if (errStr.includes("not_found") || errStr.includes("None")) { + return { ok: false, error: `Intent ${intentId} not found on-chain`, reason: "not_found" }; + } + return { ok: false, error: `Simulation error: ${errStr}`, reason: "simulation_error" }; + } + + const successSim = simulation as SorobanRpc.Api.SimulateTransactionSuccessResponse; + if (!successSim.result?.retval) { + return { ok: false, error: "Simulation returned no retval", reason: "not_found" }; + } + + const retval = successSim.result.retval; + const decoded = this.decodeIntentRetval(retval, intentId); + return { ok: true, value: decoded, rawReturnValue: retval }; + } catch (err) { + const error = err instanceof Error ? err.message : String(err); + this.logger.error(`[settlement-client] getIntent failed for ${intentId}: ${error}`); + return { ok: false, error, reason: "simulation_error" }; + } + } + + /** + * Batch-read intents from the contract with bounded concurrency. + * + * @param intentIds Intent IDs to read. + * @param concurrency Maximum number of parallel simulations. Defaults to 5. + */ + async getManyIntents( + intentIds: string[], + concurrency = 5, + ): Promise | SimulationError>> { + const results = new Map | SimulationError>(); + const queue = [...intentIds]; + + while (queue.length > 0) { + const batch = queue.splice(0, concurrency); + const settled = await Promise.allSettled( + batch.map((id) => this.getIntent(id)), + ); + for (let i = 0; i < batch.length; i++) { + const s = settled[i]; + results.set( + batch[i], + s.status === "fulfilled" + ? s.value + : { ok: false, error: String((s as PromiseRejectedResult).reason), reason: "simulation_error" }, + ); + } + } + + return results; + } + + // ── Private helpers ──────────────────────────────────────────────────────── + + private decodeIntentRetval(retval: xdr.ScVal, intentId: string): OnChainIntent { + const native = scValToNative(retval) as Record; + + return { + intentId, + user: String(native["user"] ?? ""), + solver: native["solver"] ? String(native["solver"]) : null, + state: mapContractState(native["state"] ?? native["status"]), + srcAmount: BigInt(String(native["src_amount"] ?? native["srcAmount"] ?? 0)), + minDstAmount: BigInt(String(native["min_dst_amount"] ?? native["minDstAmount"] ?? 0)), + fillAmount: native["fill_amount"] != null + ? BigInt(String(native["fill_amount"] ?? native["fillAmount"] ?? 0)) + : null, + deadline: BigInt(String(native["deadline"] ?? 0)), + }; + } +} diff --git a/src/soroban/event-ingestion.service.spec.ts b/src/soroban/event-ingestion.service.spec.ts index e69de29b..94a10a73 100644 --- a/src/soroban/event-ingestion.service.spec.ts +++ b/src/soroban/event-ingestion.service.spec.ts @@ -0,0 +1,138 @@ +import { ConfigService } from "@nestjs/config"; +import { nativeToScVal, SorobanRpc } from "@stellar/stellar-sdk"; +import { AppConfig } from "../config/configuration"; +import { + buildDedupeKey, + EventIngestionService, + parseEventIndex, +} from "./event-ingestion.service"; +import { SorobanService } from "./soroban.service"; +import { SolversService } from "../solvers/solvers.service"; +import { LeaderElectionService } from "../common/leader-election"; + +function fakeSolversService(): SolversService { + return { + confirmPenalty: jest.fn().mockResolvedValue(null), + } as unknown as SolversService; +} + +/** Minimal no-op LeaderElectionService for unit tests. */ +function noopLeaderElection(): LeaderElectionService { + return { + registerWorker: jest.fn(), + isLeader: jest.fn().mockReturnValue(true), + getState: jest.fn().mockReturnValue(null), + getAllStates: jest.fn().mockReturnValue({}), + onModuleInit: jest.fn(), + onModuleDestroy: jest.fn(), + runHeartbeatOnce: jest.fn().mockResolvedValue(undefined), + } as unknown as LeaderElectionService; +} + +function makeIntentFilledEvent( + overrides: Partial<{ ledger: number; id: string; intentId: string }> = {}, +): SorobanRpc.Api.EventResponse { + const ledger = overrides.ledger ?? 1000; + const id = overrides.id ?? `${String(ledger).padStart(10, "0")}-0000000001`; + const intentId = overrides.intentId ?? "intent-abc"; + + return { + id, + type: "contract", + ledger, + ledgerClosedAt: new Date().toISOString(), + pagingToken: id, + inSuccessfulContractCall: true, + txHash: `tx-${id}`, + topic: [nativeToScVal("intent_filled", { type: "symbol" })], + value: nativeToScVal(intentId, { type: "string" }), + } as SorobanRpc.Api.EventResponse; +} + +function makeConfigService( + settlementContractId = "CSETTLEMENT", +): ConfigService { + return { + get: (key: string) => { + if (key === "stellar.settlementContractId") return settlementContractId; + throw new Error(`unexpected config key ${key}`); + }, + } as unknown as ConfigService; +} + +describe("EventIngestionService", () => { + describe("buildDedupeKey / parseEventIndex", () => { + it("derives the event index from the trailing segment of the event id", () => { + expect(parseEventIndex("0000001000-0000000007")).toBe(7); + }); + + it("falls back to 0 for a malformed id", () => { + expect(parseEventIndex("not-a-number")).toBe(0); + }); + + it("builds distinct keys for different ledgers with the same event index", () => { + const a = buildDedupeKey({ ledgerSequence: 1000, eventIndex: 1 }); + const b = buildDedupeKey({ ledgerSequence: 1001, eventIndex: 1 }); + expect(a).not.toBe(b); + }); + }); + + describe("ingest", () => { + let service: EventIngestionService; + let sorobanService: SorobanService; + + beforeEach(() => { + sorobanService = {} as SorobanService; + service = new EventIngestionService(sorobanService, makeConfigService(), fakeSolversService(), noopLeaderElection()); + }); + + it("processes a new event exactly once", () => { + const event = makeIntentFilledEvent(); + + const result = service.ingest(event); + + expect(result).toBe(true); + expect(service.processedCount).toBe(1); + expect(service.duplicateCount).toBe(0); + }); + + it("dedupes a replayed event delivered twice (same ledger + event index)", () => { + // Simulates the same on-chain event being redelivered, e.g. because a + // poll window overlapped the previous one after a restart. + const first = makeIntentFilledEvent({ ledger: 1000 }); + const replay = makeIntentFilledEvent({ ledger: 1000 }); + + const firstResult = service.ingest(first); + const replayResult = service.ingest(replay); + + expect(firstResult).toBe(true); + expect(replayResult).toBe(false); + expect(service.processedCount).toBe(1); + expect(service.duplicateCount).toBe(1); + }); + + it("does not dedupe two distinct events for the same intent (different ledgers)", () => { + const eventA = makeIntentFilledEvent({ ledger: 1000, intentId: "intent-abc" }); + const eventB = makeIntentFilledEvent({ ledger: 1001, intentId: "intent-abc" }); + + expect(service.ingest(eventA)).toBe(true); + expect(service.ingest(eventB)).toBe(true); + expect(service.processedCount).toBe(2); + }); + + it("treats events with the same intent id but different event indices as distinct", () => { + const eventA = makeIntentFilledEvent({ + ledger: 1000, + id: "0000001000-0000000001", + }); + const eventB = makeIntentFilledEvent({ + ledger: 1000, + id: "0000001000-0000000002", + }); + + expect(service.ingest(eventA)).toBe(true); + expect(service.ingest(eventB)).toBe(true); + expect(service.processedCount).toBe(2); + }); + }); +}); diff --git a/src/soroban/event-ingestion.service.ts b/src/soroban/event-ingestion.service.ts index e69de29b..d806e589 100644 --- a/src/soroban/event-ingestion.service.ts +++ b/src/soroban/event-ingestion.service.ts @@ -0,0 +1,351 @@ +import { Injectable, OnModuleDestroy, OnModuleInit, Optional } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { scValToNative, SorobanRpc } from "@stellar/stellar-sdk"; +import { AppConfig } from "../config/configuration"; +import { logger } from "../common/logger"; +import { IntentsService } from "../intents/intents.service"; +import { MetricsService } from "../metrics/metrics.service"; +import { SorobanService } from "./soroban.service"; +import { SolversService } from "../solvers/solvers.service"; +import { LeaderElectionService, Singleton } from "../common/leader-election"; + +const POLL_INTERVAL_MS = 10_000; +const RECONCILE_INTERVAL_MS = 60_000; +const STALE_INTENT_THRESHOLD_SECONDS = 300; + +// Bound the in-memory dedupe set so long-lived processes don't leak memory. +// Once we've tracked this many keys we drop the oldest (lowest-ledger) ones, +// which is safe because we never re-poll ledgers that far behind the cursor. +const MAX_TRACKED_KEYS = 10_000; + +export interface DedupeKeyParts { + ledgerSequence: number; + eventIndex: number; +} + +// Soroban RPC event ids are "-"; we only use +// the trailing segment here since `EventResponse.ledger` is the source of +// truth for the ledger sequence. +export function parseEventIndex(eventId: string): number { + const parts = eventId.split("-"); + const index = Number(parts[parts.length - 1]); + return Number.isFinite(index) ? index : 0; +} + +export function buildDedupeKey({ ledgerSequence, eventIndex }: DedupeKeyParts): string { + return `${ledgerSequence}:${eventIndex}`; +} + +@Singleton("event-ingestion") +@Injectable() +export class EventIngestionService implements OnModuleInit, OnModuleDestroy { + private interval?: NodeJS.Timeout; + private reconcileInterval?: NodeJS.Timeout; + private readonly seenKeys = new Set(); + private readonly lastIntentUpdateById = new Map(); + private nextStartLedger?: number; + processedCount = 0; + duplicateCount = 0; + + /** + * Ledger of the newest event ingested, used to publish the ingestion-lag + * gauge (issue #481). + * + * `undefined` until the first event arrives, which is also how "we have never + * ingested anything" is distinguished from "we are perfectly current": the + * gauge is left untouched rather than being published as a confident zero. + */ + private newestIngestedLedger?: number; + + constructor( + private readonly sorobanService: SorobanService, + private readonly configService: ConfigService, + private readonly solversService: SolversService, + private readonly leaderElection: LeaderElectionService, + /** + * SLO emitters for the on-chain dashboard. `@Optional()` so the unit tests + * that construct this service directly do not need a metrics registry; + * `MetricsModule` is `@Global()`, so the running application always has one. + */ + @Optional() private readonly metricsService?: MetricsService, + /** + * Read-only lookup used to time an off-chain fill against its on-chain + * confirmation. Optional for the same reason as `metricsService`, and + * because `EventIngestionService` is also instantiated in `SorobanModule`, + * where the intent service is reached through a `forwardRef`. + */ + @Optional() private readonly intentsService?: IntentsService, + ) {} + + onModuleInit() { + this.leaderElection.registerWorker("event-ingestion", (isLeader, _token) => { + if (isLeader) { + logger.info("[event-ingestion] became leader — starting polling intervals"); + this.startIntervals(); + } else { + logger.info("[event-ingestion] lost leadership — stopping polling intervals"); + this.stopIntervals(); + } + }); + } + + onModuleDestroy() { + this.stopIntervals(); + } + + private startIntervals(): void { + if (this.interval) return; // already running + this.interval = setInterval(() => { + this.poll().catch((err) => + logger.error(`[event-ingestion] poll failed: ${err instanceof Error ? err.message : String(err)}`), + ); + }, POLL_INTERVAL_MS); + + this.reconcileInterval = setInterval(() => { + this.reconcileStaleIntents().catch((err) => { + logger.error( + `[event-ingestion] stale-intent reconciliation failed: ${err instanceof Error ? err.message : String(err)}`, + ); + }); + }, RECONCILE_INTERVAL_MS); + } + + private stopIntervals(): void { + if (this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + if (this.reconcileInterval) { + clearInterval(this.reconcileInterval); + this.reconcileInterval = undefined; + } + } + + async poll(): Promise { + const settlementContractId = this.configService.get("stellar.settlementContractId", { infer: true }); + if (!settlementContractId) return; + + let startLedger = this.nextStartLedger; + if (startLedger === undefined) { + const latest = await this.sorobanService.getLatestLedger(); + startLedger = latest.sequence; + } + + const response = await this.sorobanService.getEvents({ + startLedger, + filters: [{ type: "contract", contractIds: [settlementContractId] }], + }); + + for (const event of response.events) { + this.ingest(event); + } + + this.nextStartLedger = response.latestLedger + 1; + await this.publishIngestionLag(); + } + + /** + * Publish `vortex_event_ingestion_lag_seconds`: how far behind the chain head + * this process is (issue #481). + * + * The lag is measured against the close time of the newest ledger we actually + * ingested an event from, not against the newest ledger that exists — the + * former is the number that says whether a client is seeing settlement + * promptly, and it is the one `VortexIngestionLagHigh` is built to alert on. + * + * One extra RPC per poll, and only once at least one event has been seen. + * Any failure leaves the gauge at its previous value rather than writing a + * bogus zero: a metric that snaps to 0 during an RPC outage is worse than one + * that goes stale. + */ + private async publishIngestionLag(): Promise { + if (!this.metricsService || this.newestIngestedLedger === undefined) return; + try { + const header = await this.sorobanService.getLedger(this.newestIngestedLedger); + const closeTime = Number(header?.header?.closeTime); + if (!Number.isFinite(closeTime) || closeTime <= 0) { + logger.debug( + `[event-ingestion] ledger ${this.newestIngestedLedger} reported no usable closeTime; leaving ingestion lag unchanged`, + ); + return; + } + const lagSeconds = Math.max(0, Math.floor(Date.now() / 1000) - closeTime); + this.metricsService.setIngestionLag(lagSeconds); + } catch (err) { + logger.debug( + `[event-ingestion] could not read ledger ${this.newestIngestedLedger} for the ingestion-lag gauge: ${ + err instanceof Error ? err.message : String(err) + }`, + ); + } + } + + // Skips events already seen at this ledger+index, which protects against + // redelivery after a restart (cursor rewinds) or overlapping poll windows. + ingest(event: SorobanRpc.Api.EventResponse): boolean { + const dedupeKey = buildDedupeKey({ + ledgerSequence: event.ledger, + eventIndex: parseEventIndex(event.id), + }); + + if (this.seenKeys.has(dedupeKey)) { + this.duplicateCount++; + return false; + } + + this.markSeen(dedupeKey); + this.processEvent(event); + this.processedCount++; + if (this.newestIngestedLedger === undefined || event.ledger > this.newestIngestedLedger) { + this.newestIngestedLedger = event.ledger; + } + return true; + } + + private markSeen(dedupeKey: string) { + this.seenKeys.add(dedupeKey); + if (this.seenKeys.size > MAX_TRACKED_KEYS) { + const oldest = this.seenKeys.values().next().value; + if (oldest !== undefined) this.seenKeys.delete(oldest); + } + } + + private processEvent(event: SorobanRpc.Api.EventResponse): void { + const topic = event.topic.map((scVal) => { + try { + return scValToNative(scVal); + } catch { + return undefined; + } + }); + + const eventName = typeof topic[0] === "string" ? topic[0] : undefined; + if (eventName === "intent_filled") { + // Fire-and-forget for the same reason as `solver_slashed`: timing a + // confirmation must never stall the poll loop, and a lookup failure is + // logged rather than propagated. + this.handleIntentFilled(event, topic).catch((err) => + logger.error( + `[event-ingestion] intent_filled confirmation timing failed at ledger=${event.ledger}: ${ + (err as Error).message + }`, + ), + ); + } else if (eventName === "solver_slashed") { + // Fire-and-forget: penalty confirmation is non-blocking relative to + // ingestion — a reconciliation failure is logged but never stalls the + // poll loop. + this.handleSolverSlashed(event, topic).catch((err) => + console.error( + `[event-ingestion] solver_slashed reconciliation failed at ledger=${event.ledger}: ${(err as Error).message}`, + ), + ); + } + + const intentId = typeof topic[1] === "string" ? topic[1] : undefined; + if (intentId) { + this.lastIntentUpdateById.set(intentId, Math.floor(Date.now() / 1000)); + } + } + + /** + * Record a confirmed on-chain fill and time it against the off-chain fill. + * + * `vortex_tx_confirmation_duration_seconds` is the settlement-pipeline SLO + * SLI: how long a fill takes to appear on chain after the off-chain path + * recorded it. The off-chain timestamp is the intent's own `filledAt`, so the + * two sides are joined by intent id — an event for an intent this process has + * never seen, or one still awaiting its off-chain write, is skipped rather + * than recorded as an implausibly large (or negative) latency. + */ + private async handleIntentFilled( + event: SorobanRpc.Api.EventResponse, + topic: unknown[], + ): Promise { + logger.info( + `[event-ingestion] intent_filled event at ledger=${event.ledger} txHash=${event.txHash} topic=${JSON.stringify(topic)}`, + ); + + const intentId = typeof topic[1] === "string" ? topic[1] : undefined; + if (!intentId || !this.metricsService || !this.intentsService) return; + + const intent = await this.intentsService.get(intentId); + const filledAt = intent?.filledAt; + if (typeof filledAt !== "number" || filledAt <= 0) { + logger.debug( + `[event-ingestion] intent_filled for ${intentId} has no off-chain fill timestamp yet; not observing confirmation latency`, + ); + return; + } + + const confirmationSeconds = Math.max(0, Math.floor(Date.now() / 1000) - filledAt); + this.metricsService.observeTxConfirmation(confirmationSeconds); + } + + private async reconcileStaleIntents(): Promise { + const now = Math.floor(Date.now() / 1000); + for (const [intentId, lastUpdated] of this.lastIntentUpdateById.entries()) { + if (now - lastUpdated <= STALE_INTENT_THRESHOLD_SECONDS) continue; + + logger.warn( + `[event-ingestion] stale intent state detected for intent=${intentId} lastUpdatedSecondsAgo=${now - lastUpdated}; polling chain for reconciliation`, + ); + + const settlementContractId = this.configService.get("stellar.settlementContractId", { infer: true }); + if (!settlementContractId) continue; + + const latestLedger = await this.sorobanService.getLatestLedger(); + await this.sorobanService.getEvents({ + startLedger: Math.max(1, latestLedger.sequence - 1), + filters: [{ type: "contract", contractIds: [settlementContractId] }], + }); + + this.lastIntentUpdateById.set(intentId, Math.floor(Date.now() / 1000)); + } + } + + /** + * Handles a solver_slashed event emitted by the Soroban solver-registry + * contract once a slash transaction is confirmed on-chain. + * + * Expected topic layout (positions 1+ after the event name at position 0): + * topic[1] — solver address (string) + * topic[2] — intentId (string) + * topic[3] — slash amount (string or bigint) + * + * Calls SolversService.confirmPenalty() which reconciles bondAmount and + * marks the penalty as "confirmed" in the in-memory pendingPenalties map. + * + * If topic values cannot be extracted (malformed event), a warning is logged + * and the event is silently skipped — this protects against a bad contract + * event bringing down the ingestion loop. + */ + private async handleSolverSlashed( + event: SorobanRpc.Api.EventResponse, + topic: unknown[], + ): Promise { + const solverAddress = typeof topic[1] === "string" ? topic[1] : undefined; + const intentId = typeof topic[2] === "string" ? topic[2] : undefined; + const rawAmount = topic[3]; + const slashAmount = + typeof rawAmount === "bigint" + ? rawAmount.toString() + : typeof rawAmount === "string" + ? rawAmount + : undefined; + + if (!solverAddress || !intentId || !slashAmount) { + console.warn( + `[event-ingestion] solver_slashed event at ledger=${event.ledger} has unexpected topic shape; skipping reconciliation`, + { solverAddress, intentId, slashAmount, rawTopic: topic }, + ); + return; + } + + console.log( + `[event-ingestion] solver_slashed confirmed: solver=${solverAddress} intentId=${intentId} slashAmount=${slashAmount} ledger=${event.ledger}`, + ); + + await this.solversService.confirmPenalty(intentId, slashAmount); + } +} diff --git a/src/soroban/fill-verifier.service.spec.ts b/src/soroban/fill-verifier.service.spec.ts index e69de29b..0bd42e76 100644 --- a/src/soroban/fill-verifier.service.spec.ts +++ b/src/soroban/fill-verifier.service.spec.ts @@ -0,0 +1,63 @@ +import { Asset } from "@stellar/stellar-sdk"; +import { FillVerifierService } from "./fill-verifier.service"; +import { NETWORK_PASSPHRASES } from "../config/configuration"; +import { Intent } from "../intents/intents.types"; + +describe("FillVerifierService", () => { + const intent = { + intentId: "intent-123", + user: "GDESTINATION", + dstToken: { contract: Asset.native().contractId(NETWORK_PASSPHRASES.testnet), decimals: 7 }, + minDstAmount: "12000000", + } as Intent; + const config = { + get: (key: string) => key === "stellar.network" ? "testnet" : "https://horizon.test", + } as never; + const service = new FillVerifierService(config); + const originalFetch = global.fetch; + + afterEach(() => { global.fetch = originalFetch; }); + + it("uses the delivered amount for path payments before verifying the minimum", async () => { + global.fetch = jest.fn() + .mockResolvedValueOnce(new Response(JSON.stringify({ + successful: true, + memo_type: "text", + memo: intent.intentId, + _links: { operations: { href: "ignored" } }, + }), { status: 200 })) + .mockResolvedValueOnce(new Response(JSON.stringify({ _embedded: { records: [{ + type: "path_payment_strict_send", + to: intent.user, + asset_type: "native", + destination_amount: "1.3", + amount: "2.0", + }] } }), { status: 200 })); + + await expect(service.verify("a".repeat(64), intent)).resolves.toEqual({ + status: "verified", + deliveredAmount: "13000000", + operation: "path_payment_strict_send", + }); + }); + + it("keeps a transaction not yet indexed by Horizon retryable", async () => { + global.fetch = jest.fn().mockResolvedValueOnce(new Response("{}", { status: 404 })); + await expect(service.verify("b".repeat(64), intent)).resolves.toEqual({ + status: "pending", + reason: "not_indexed", + }); + }); + + it("rejects a successful transaction without the intent binding memo", async () => { + global.fetch = jest.fn().mockResolvedValueOnce(new Response(JSON.stringify({ + successful: true, + memo_type: "text", + memo: "another-intent", + }), { status: 200 })); + await expect(service.verify("c".repeat(64), intent)).resolves.toEqual({ + status: "rejected", + reason: "intent_memo_mismatch", + }); + }); +}); diff --git a/src/soroban/fill-verifier.service.ts b/src/soroban/fill-verifier.service.ts index e69de29b..c29041ca 100644 --- a/src/soroban/fill-verifier.service.ts +++ b/src/soroban/fill-verifier.service.ts @@ -0,0 +1,89 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Asset } from "@stellar/stellar-sdk"; +import { AppConfig, NETWORK_PASSPHRASES } from "../config/configuration"; +import { Intent } from "../intents/intents.types"; + +/** A structured independent verdict for a submitted Stellar fill transaction. */ +export type FillVerificationVerdict = + | { status: "verified"; deliveredAmount: string; operation: string } + | { status: "pending"; reason: "not_indexed" | "horizon_unavailable" } + | { status: "rejected"; reason: string }; + +type HorizonTransaction = { + successful?: boolean; + memo_type?: string; + memo?: string; + _links?: { operations?: { href?: string } }; +}; + +/** Independently checks Horizon's indexed Stellar transaction and payment operations. */ +@Injectable() +export class FillVerifierService { + constructor(private readonly config: ConfigService) {} + + /** + * Verify the transaction against the persisted intent. Unknown/indexing errors + * remain retryable; malformed or mismatched transactions are definitive. + */ + async verify(txHash: string, intent: Intent): Promise { + const base = this.config.get("stellar.horizonUrl", { infer: true }).replace(/\/$/, ""); + let transaction: HorizonTransaction; + try { + const response = await fetch(`${base}/transactions/${encodeURIComponent(txHash)}`); + if (response.status === 404) return { status: "pending", reason: "not_indexed" }; + if (!response.ok) return { status: "pending", reason: "horizon_unavailable" }; + transaction = (await response.json()) as HorizonTransaction; + } catch { + return { status: "pending", reason: "horizon_unavailable" }; + } + + if (transaction.successful !== true) return { status: "rejected", reason: "transaction_failed" }; + if (transaction.memo_type !== "text" || transaction.memo !== intent.intentId) { + return { status: "rejected", reason: "intent_memo_mismatch" }; + } + if (!transaction._links?.operations?.href) return { status: "rejected", reason: "operations_missing" }; + try { + const response = await fetch(`${base}/transactions/${encodeURIComponent(txHash)}/operations?limit=200&order=asc`); + if (!response.ok) return { status: "pending", reason: "horizon_unavailable" }; + const body = (await response.json()) as { _embedded?: { records?: Array> } }; + const operations = body._embedded?.records ?? []; + for (const operation of operations) { + const type = operation.type as string; + if (!["payment", "path_payment_strict_send", "path_payment_strict_receive"].includes(type)) continue; + if (operation.source_account && operation.source_account !== intent.solver) continue; + const destination = String(operation.to ?? ""); + const network = this.config.get("stellar.network", { infer: true }); + let assetContractId: string; + try { + assetContractId = operation.asset_type === "native" + ? Asset.native().contractId(NETWORK_PASSPHRASES[network]) + : new Asset(String(operation.asset_code ?? ""), String(operation.asset_issuer ?? "")) + .contractId(NETWORK_PASSPHRASES[network]); + } catch { + continue; + } + if (destination !== intent.user) continue; + // Horizon exposes classic assets by code/issuer. Soroban contract IDs + // require Soroban RPC event verification, and are never credited here. + if (assetContractId !== intent.dstToken.contract) continue; + const raw = type.startsWith("path_payment_") ? operation.destination_amount : operation.amount; + if (typeof raw !== "string" || !/^\d+(\.\d+)?$/.test(raw)) continue; + const delivered = decimalToBaseUnits(raw, intent.dstToken.decimals); + if (BigInt(delivered) < BigInt(intent.minDstAmount)) { + return { status: "rejected", reason: "insufficient_delivered_amount" }; + } + return { status: "verified", deliveredAmount: delivered, operation: type }; + } + return { status: "rejected", reason: "matching_payment_missing" }; + } catch { + return { status: "pending", reason: "horizon_unavailable" }; + } + } +} + +function decimalToBaseUnits(value: string, decimals: number): string { + const [whole, fraction = ""] = value.split("."); + if (fraction.length > decimals) throw new Error("Horizon amount precision exceeds token decimals"); + return (BigInt(whole) * 10n ** BigInt(decimals) + BigInt((fraction + "0".repeat(decimals)).slice(0, decimals) || "0")).toString(); +} diff --git a/src/soroban/signer.service.spec.ts b/src/soroban/signer.service.spec.ts index e69de29b..80df543d 100644 --- a/src/soroban/signer.service.spec.ts +++ b/src/soroban/signer.service.spec.ts @@ -0,0 +1,215 @@ +import { inspect } from "node:util"; +import { Account, FeeBumpTransaction, Keypair, Networks, Operation, Transaction, TransactionBuilder, xdr } from "@stellar/stellar-sdk"; +import { AppConfig } from "../config/configuration"; +import { SignerService } from "./signer.service"; +import { SorobanService } from "./soroban.service"; +import { ISigner } from "./signers/signer.interface"; +import { LocalKeypairSigner } from "./signers/local-keypair.signer"; +import { findSensitiveKeyMaterial } from "./redaction"; + +/** + * Builds a local-keypair signing backend over a stubbed config, i.e. the same + * ISigner the SIGNER_TOKEN provider hands to SignerService in the app. + */ +function signerWith(signerSecretKey: string, network: AppConfig["stellar"]["network"] = "testnet"): ISigner { + const values: Record = { + "stellar.signingKey": signerSecretKey, + "stellar.network": network, + }; + const configService = { get: (path: string) => values[path] } as ConfigService; + return new LocalKeypairSigner(configService); +import { findSensitiveKeyMaterial } from "./redaction"; + +/** + * Build a mock ISigner backed by an optional keypair. + * + * SignerService was refactored (issue #400) to delegate to an ISigner backend, + * so the tests construct it with a mock backend rather than a ConfigService. + */ +function fakeSigner(keypair?: Keypair, network: AppConfig["stellar"]["network"] = "testnet") { + const passphrase = + network === "mainnet" ? Networks.PUBLIC : network === "futurenet" ? Networks.FUTURENET : Networks.TESTNET; + return { + // Mirrors LocalKeypairSigner: an unconfigured backend has no public key and + // throws a clear, secret-free error when one is requested. + publicKey: () => { + if (!keypair) throw new Error("Signer is not configured: no signing key is available"); + return keypair.publicKey(); + }, + networkPassphrase: () => passphrase, + signTransaction: async (tx: T): Promise => { + if (keypair) tx.sign(keypair); + return tx; + }, + signAuthEntry: async (entry: xdr.SorobanAuthorizationEntry): Promise => entry, + } as unknown as ISigner; +} + +function fakeSorobanService(startingSequence = "100") { + return { + getAccount: jest.fn().mockImplementation(async (publicKey: string) => new Account(publicKey, startingSequence)), + } as unknown as jest.Mocked; +} + +describe("SignerService", () => { + it("reports unconfigured when no secret is set", () => { + const service = new SignerService(signerWith(""), fakeSorobanService()); + const service = new SignerService(fakeSigner(), fakeSorobanService()); + expect(service.isConfigured()).toBe(false); + }); + + it("throws a clear, secret-free error when signing without a configured key", () => { + const service = new SignerService(signerWith(""), fakeSorobanService()); + expect(() => service.getPublicKey()).toThrow(/SOROBAN_SIGNING_KEY/); + const service = new SignerService(fakeSigner(), fakeSorobanService()); + expect(() => service.getPublicKey()).toThrow(/not configured|signing key/i); + // The message must stay secret-free (no strkey / seed shape). + let message = ""; + try { + service.getPublicKey(); + } catch (err) { + message = (err as Error).message; + } + expect(message).not.toMatch(/^S[A-Z2-7]{55}$/); + }); + + it("derives the public key from the configured secret", () => { + const keypair = Keypair.random(); + const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); + const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); + + expect(service.isConfigured()).toBe(true); + expect(service.getPublicKey()).toBe(keypair.publicKey()); + }); + + it("maps network config to the right passphrase", () => { + const soroban = fakeSorobanService(); + expect(new SignerService(signerWith("", "testnet"), soroban).getNetworkPassphrase()).toBe(Networks.TESTNET); + expect(new SignerService(signerWith("", "futurenet"), soroban).getNetworkPassphrase()).toBe(Networks.FUTURENET); + expect(new SignerService(signerWith("", "mainnet"), soroban).getNetworkPassphrase()).toBe(Networks.PUBLIC); + expect(new SignerService(fakeSigner(undefined, "testnet"), soroban).getNetworkPassphrase()).toBe(Networks.TESTNET); + expect(new SignerService(fakeSigner(undefined, "futurenet"), soroban).getNetworkPassphrase()).toBe(Networks.FUTURENET); + expect(new SignerService(fakeSigner(undefined, "mainnet"), soroban).getNetworkPassphrase()).toBe(Networks.PUBLIC); + }); + + it("signs a transaction with the configured key", async () => { + const keypair = Keypair.random(); + const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); + const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); + + const account = new Account(keypair.publicKey(), "1"); + const tx = new TransactionBuilder(account, { fee: "100", networkPassphrase: Networks.TESTNET }) + .addOperation(Operation.bumpSequence({ bumpTo: "2" })) + .setTimeout(30) + .build(); + + expect(tx.signatures).toHaveLength(0); + const signed = await service.sign(tx); + expect(signed.signatures).toHaveLength(1); + }); + + it("never includes the raw secret in string/JSON/inspect representations", () => { + const keypair = Keypair.random(); + const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); + const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); + + const secret = keypair.secret(); + expect(String(service)).not.toContain(secret); + expect(JSON.stringify(service)).not.toContain(secret); + expect(inspect(service)).not.toContain(secret); + expect(findSensitiveKeyMaterial(service)).toEqual([]); + }); + + it("exposes no raw Stellar secret in serialized error payloads", () => { + const keypair = Keypair.random(); + const secret = keypair.secret(); + const payload = { + error: "transaction simulation failed", + signer: { secretKey: secret, publicKey: keypair.publicKey() }, + }; + + expect(findSensitiveKeyMaterial(payload)).toContain(secret); + expect(findSensitiveKeyMaterial({ error: "ok" })).toEqual([]); + }); + + describe("withNextSequence", () => { + it("fetches the starting sequence once and increments it locally", async () => { + const keypair = Keypair.random(); + const soroban = fakeSorobanService("100"); + const service = new SignerService(signerWith(keypair.secret()), soroban); + const service = new SignerService(fakeSigner(keypair), soroban); + + const first = await service.withNextSequence(async (sequence) => sequence); + const second = await service.withNextSequence(async (sequence) => sequence); + const third = await service.withNextSequence(async (sequence) => sequence); + + expect([first, second, third]).toEqual(["101", "102", "103"]); + expect(soroban.getAccount).toHaveBeenCalledTimes(1); + }); + + it("hands out a distinct, gap-free sequence to every concurrent caller", async () => { + const keypair = Keypair.random(); + const soroban = fakeSorobanService("0"); + const service = new SignerService(signerWith(keypair.secret()), soroban); + const service = new SignerService(fakeSigner(keypair), soroban); + + const results = await Promise.all( + Array.from({ length: 20 }, () => service.withNextSequence(async (sequence) => sequence)), + ); + + const numeric = results.map(Number).sort((a, b) => a - b); + expect(new Set(numeric).size).toBe(20); // no two callers got the same sequence + expect(numeric).toEqual(Array.from({ length: 20 }, (_, i) => i + 1)); // 1..20, no gaps + }); + + it("runs callers strictly one at a time, in call order", async () => { + const keypair = Keypair.random(); + const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService("0")); + const service = new SignerService(fakeSigner(keypair), fakeSorobanService("0")); + const order: number[] = []; + + const slow = service.withNextSequence(async () => { + await new Promise((resolve) => setTimeout(resolve, 30)); + order.push(1); + }); + const fast = service.withNextSequence(async () => { + order.push(2); + }); + + await Promise.all([slow, fast]); + expect(order).toEqual([1, 2]); // fast waited for slow despite finishing faster on its own + }); + + it("drops the cached sequence after a failure so the next call re-syncs from the network", async () => { + const keypair = Keypair.random(); + const soroban = fakeSorobanService("100"); + const service = new SignerService(signerWith(keypair.secret()), soroban); + const service = new SignerService(fakeSigner(keypair), soroban); + + await expect( + service.withNextSequence(async () => { + throw new Error("submission failed"); + }), + ).rejects.toThrow("submission failed"); + + const next = await service.withNextSequence(async (sequence) => sequence); + expect(next).toBe("101"); + expect(soroban.getAccount).toHaveBeenCalledTimes(2); // re-fetched after the failure + }); + + it("does not let a failed caller block callers queued behind it", async () => { + const keypair = Keypair.random(); + const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService("0")); + const service = new SignerService(fakeSigner(keypair), fakeSorobanService("0")); + + const failing = service.withNextSequence(async () => { + throw new Error("boom"); + }); + const following = service.withNextSequence(async (sequence) => sequence); + + await expect(failing).rejects.toThrow("boom"); + // cache was dropped after the failure, so this re-syncs from the network (still "0") and gets "1" + await expect(following).resolves.toBe("1"); + }); + }); +}); diff --git a/src/soroban/signer.service.ts b/src/soroban/signer.service.ts index e69de29b..fe99f42b 100644 --- a/src/soroban/signer.service.ts +++ b/src/soroban/signer.service.ts @@ -0,0 +1,132 @@ +/** + * SignerService (issue #400 — refactored) + * ───────────────────────────────────────── + * Facade that delegates all signing operations to the injected ISigner backend + * (LocalKeypairSigner or VaultTransitSigner). Existing callers continue to + * use SignerService unchanged — they do not need to know which backend is + * active. + * + * Sequence-number management lives here (not in the backend) because it is + * network-state rather than key-material. The in-process lock and sequence + * cache are independent of the signing implementation. + * + * The raw secret key is no longer held by this service — it is owned + * exclusively by LocalKeypairSigner (and never exists in memory at all when + * VaultTransitSigner is used). The toString / toJSON / inspect overrides + * here therefore only report the active backend name, never key material. + */ + +import { Inject, Injectable, Logger, Optional } from "@nestjs/common"; +import { FeeBumpTransaction, Transaction, xdr } from "@stellar/stellar-sdk"; +import { SorobanService } from "./soroban.service"; +import { ISigner, SIGNER_TOKEN } from "./signers/signer.interface"; + +const REDACTED = "[redacted]"; + +@Injectable() +export class SignerService { + private readonly logger = new Logger(SignerService.name); + + // Chains sequence acquisitions so they run one at a time, in call order. + private sequenceLock: Promise = Promise.resolve(); + private cachedSequence: bigint | null = null; + + constructor( + @Inject(SIGNER_TOKEN) private readonly backend: ISigner, + private readonly sorobanService: SorobanService, + ) {} + + // ── ISigner delegation ──────────────────────────────────────────────────── + + /** Returns the signing account's Stellar public key (G-address). */ + getPublicKey(): string { + return this.backend.publicKey(); + } + + /** Returns the Stellar network passphrase this service was configured for. */ + getNetworkPassphrase(): string { + return this.backend.networkPassphrase(); + } + + /** + * Sign a transaction. Delegates to the active backend. + * + * @deprecated Prefer `withNextSequence` for new Soroban transaction submissions. + */ + async sign(transaction: T): Promise { + return this.backend.signTransaction(transaction); + } + + /** + * Sign a Soroban auth entry. Delegates to the active backend. + */ + async signAuthEntry(entry: xdr.SorobanAuthorizationEntry): Promise { + return this.backend.signAuthEntry(entry); + } + + /** Whether a signing key has been configured. False in dev/test by default. */ + isConfigured(): boolean { + try { + return this.backend.publicKey().length > 0; + } catch { + return false; + } + } + + // ── Sequence-number management ──────────────────────────────────────────── + + /** + * Runs `fn` with the next sequence number for the signing account, holding + * an in-process lock for the duration so no concurrent caller gets the same + * sequence number. + * + * The sequence is fetched from the network once and cached; subsequent calls + * increment the cached value locally. If `fn` throws the cache is dropped + * so the next call re-syncs from the network rather than drifting. + */ + async withNextSequence(fn: (sequence: string) => Promise): Promise { + let releaseLock!: () => void; + const previous = this.sequenceLock; + this.sequenceLock = new Promise((resolve) => { + releaseLock = resolve; + }); + await previous; + + try { + const sequence = await this.nextSequence(); + return await fn(sequence); + } catch (err) { + this.cachedSequence = null; + throw err; + } finally { + releaseLock(); + } + } + + private async nextSequence(): Promise { + if (this.cachedSequence === null) { + const account = await this.sorobanService.getAccount(this.getPublicKey()); + this.cachedSequence = BigInt(account.sequenceNumber()); + } + this.cachedSequence += 1n; + return this.cachedSequence.toString(); + } + + // ── Redaction guarantees ────────────────────────────────────────────────── + + toString(): string { + return `SignerService(backend=${this.backend.constructor.name}, publicKey=${this.isConfigured() ? this.getPublicKey() : "unconfigured"}, secretKey=${REDACTED})`; + } + + toJSON(): unknown { + return { + backend: this.backend.constructor.name, + publicKey: this.isConfigured() ? this.getPublicKey() : null, + secretKey: REDACTED, + }; + } + + [Symbol.for("nodejs.util.inspect.custom")](): string { + return this.toString(); + } +} diff --git a/src/soroban/solver-registry.service.spec.ts b/src/soroban/solver-registry.service.spec.ts index e69de29b..1f9a7379 100644 --- a/src/soroban/solver-registry.service.spec.ts +++ b/src/soroban/solver-registry.service.spec.ts @@ -0,0 +1,212 @@ +import { ConfigService } from "@nestjs/config"; +import { SolverRegistryService } from "./solver-registry.service"; +import { AppConfig } from "../config/configuration"; + +function makeConfigService( + overrides: Partial = {}, + appOverrides: Partial> = {}, +) { + const stellar: AppConfig["stellar"] = { + network: "testnet", + sorobanRpcUrl: "https://soroban-testnet.stellar.org", + horizonUrl: "https://horizon-testnet.stellar.org", + settlementContractId: "", + solverRegistryContractId: "", + signerSecretKey: "", + signingKey: "", + feePercentile: "p50", + maxFeeStroops: 1000000, + channelPoolSize: 8, + channelSecretKeys: "", + ...overrides, + }; + const config: AppConfig = { + nodeEnv: "test", + port: 4000, + databaseUrl: "postgresql://vortex:vortex@localhost:5432/vortex?schema=public", + stellar, + treasury: { address: "" }, + onchainIntentsEnabled: false, + intentRetentionDays: 30, + intentRetentionSweepMs: 60000, + // Default to dry-run true for tests (safe default) + onchainDryRun: appOverrides.onchainDryRun ?? true, + corsOrigin: "*", + wsMaxConnections: 1000, + wsBackplane: "memory", + redisUrl: "redis://localhost:6379", + // Resource-exhaustion limits (issue #476) — test defaults + jsonMaxDepth: 10, + wsMaxFilterChains: 20, + wsMaxSubscriptions: 10, + dbQueryTimeoutMs: 5000, + dbBatchQueryTimeoutMs: 10000, + dbStatsQueryTimeoutMs: 15000, + // Emergency kill-switch (issue #477) — no operator token in unit tests, so + // the control plane stays disabled. + killswitch: { + operatorToken: "", + redisUrl: "", + pollMs: 2000, + }, + shadow: { + enabled: false, + sampleRate: 1, + queueMax: 256, + concurrency: 4, + sourceAccount: "", + }, + governance: { + paramsContractId: "", + paramsPollIntervalMs: 30_000, + }, + leaderElection: { + enabled: false, + heartbeatMs: 5000, + }, + processRole: "all", + jobs: { driver: "memory", shutdownTimeoutMs: 25000 }, + flags: { pubsub: "memory", refreshMs: 30000, overrides: "" }, + adminApiKeys: "", + guardianContractId: "", + canaryAddresses: [], + datasets: { + enabled: false, + anonymize: true, + salt: "", + saltRotationHours: 24, + saltRetentionWindows: 2, + publicBucket: "vortex-public-datasets", + storageKind: "local", + localDir: ".datasets", + }, + secrets: { + provider: "env", + refreshIntervalMs: 60000, + extra: "", + ws: { + maxPayloadBytes: 16384, + maxConnectionsPerIp: 20, + trustProxyHops: 0, + rateLimitPerSec: 10, + rateLimitBurst: 20, + rateLimitMaxViolations: 5, + outboundQueueMax: 1000, + outboundBufferBytes: 1048576, + slowConsumerPolicy: "drop_oldest", + }, + authJwtSecret: "", + rateLimitLocalPruneMs: 60000, + rateLimitRedisUrl: "", + credentialRevocationPubsub: "memory", + sse: { + heartbeatMs: 15000, + maxBufferBytes: 1048576, + }, + datasets: { + enabled: false, + anonymize: true, + salt: "", + saltRotationHours: 24, + saltRetentionWindows: 2, + publicBucket: "", + storageKind: "memory", + localDir: "", + }, + treasury: { + address: "", + }, + shadow: { + enabled: false, + sampleRate: 1, + queueMax: 256, + concurrency: 4, + sourceAccount: "", + }, + health: { + roles: ["api", "ws", "worker"], + checkIntervalMs: 5000, + readyFailureThreshold: 3, + readySuccessThreshold: 2, + eventLoopMaxLagMs: 1000, + rpcHealthUrls: ["https://soroban-testnet.stellar.org"], + }, + }; + return { + get: (key: string) => { + if (key === "onchainDryRun") return config.onchainDryRun; + const parts = key.split("."); + return (config as unknown as Record)[parts[0]] && parts[0] === "stellar" + ? (stellar as unknown as Record)[parts[1]] + : undefined; + }, + } as unknown as ConfigService; +} + +describe("SolverRegistryService", () => { + it("is not configured when the contract id and signing key are both empty (default)", () => { + const service = new SolverRegistryService(makeConfigService()); + expect(service.isConfigured).toBe(false); + }); + + it("is not configured when only the contract id is set", () => { + const service = new SolverRegistryService( + makeConfigService({ solverRegistryContractId: "CABCDEF" }), + ); + expect(service.isConfigured).toBe(false); + }); + + it("no-ops without contacting the network when unconfigured (dry-run=true)", async () => { + const service = new SolverRegistryService(makeConfigService()); + const result = await service.slashSolver({ + solverAddress: "GSOLVER", + intentId: "intent-1", + reason: "missed deadline", + }); + + expect(result.submitted).toBe(false); + expect(result.simulated).toBe(false); + // In dry-run mode, dryRun flag is true + expect(result.dryRun).toBe(true); + }); +}); + +// ── #260: dry-run flag behaviour ───────────────────────────────────────────── + +describe("SolverRegistryService — dry-run flag (#260)", () => { + it("returns dryRun:true without simulating when ONCHAIN_DRY_RUN=true", async () => { + const service = new SolverRegistryService( + makeConfigService( + { solverRegistryContractId: "CTEST123", signingKey: "S" + "A".repeat(55) }, + { onchainDryRun: true }, + ), + ); + + const result = await service.slashSolver({ + solverAddress: "GSOLVER", + intentId: "intent-1", + reason: "missed deadline", + }); + + expect(result.submitted).toBe(false); + expect(result.dryRun).toBe(true); + expect(result.detail).toMatch(/ONCHAIN_DRY_RUN=true/); + }); + + it("returns dryRun:false when ONCHAIN_DRY_RUN=false and service is not fully configured", async () => { + // With dryRun=false but contract not configured → falls through to no-op + const service = new SolverRegistryService( + makeConfigService({}, { onchainDryRun: false }), + ); + + const result = await service.slashSolver({ + solverAddress: "GSOLVER", + intentId: "intent-1", + reason: "missed deadline", + }); + + expect(result.submitted).toBe(false); + expect(result.dryRun).toBe(false); + expect(result.detail).toMatch(/not configured/i); + }); +}); diff --git a/src/soroban/soroban.controller.spec.ts b/src/soroban/soroban.controller.spec.ts index e69de29b..667fb33b 100644 --- a/src/soroban/soroban.controller.spec.ts +++ b/src/soroban/soroban.controller.spec.ts @@ -0,0 +1,174 @@ +import { BadRequestException } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; +import { SorobanController } from "./soroban.controller"; +import { SorobanService } from "./soroban.service"; + +// --------------------------------------------------------------------------- +// Mock SorobanService — we only want to verify the controller wires correctly. +// --------------------------------------------------------------------------- + +const mockSorobanService = { + getHealth: jest.fn(), + getLatestLedger: jest.fn(), + getNetwork: jest.fn(), + getAccount: jest.fn(), +}; + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("SorobanController", () => { + let controller: SorobanController; + + beforeEach(async () => { + // `reset`, not `clear`: `mockResolvedValueOnce` queues survive + // `clearAllMocks`, so a leftover one-shot from the previous test would be + // served before the rejection this test installs. + // `resetAllMocks`, not `clearAllMocks`: `clearAllMocks` only drops recorded + // calls and leaves queued `mockResolvedValueOnce` / `mockRejectedValueOnce` + // implementations in place, so a leftover value from an earlier test is + // served ahead of the one the current test queued. + jest.resetAllMocks(); + + const module: TestingModule = await Test.createTestingModule({ + controllers: [SorobanController], + providers: [{ provide: SorobanService, useValue: mockSorobanService }], + }).compile(); + + controller = module.get(SorobanController); + }); + + // ------------------------------------------------------------------------- + // Construction + // ------------------------------------------------------------------------- + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + // ------------------------------------------------------------------------- + // getHealth + // ------------------------------------------------------------------------- + + // The pre-existing test references `getHealth` which was renamed `getChainHealth`. + // Patch the mock and the calls to use the new method name. + describe("getChainHealth", () => { + it("calls sorobanService.getEndpointHealthReport and returns endpoints", async () => { + const mockEndpoints = [{ url: "https://rpc.example.com", state: "closed", score: 1, errorRate: 0, p95LatencyMs: 0, ledgerLag: 0, lastSuccessAt: null, lastErrorAt: null, consecutiveErrors: 0 }]; + const svc = controller["sorobanService"] as unknown as { getEndpointHealthReport: jest.Mock }; + svc.getEndpointHealthReport = jest.fn().mockReturnValue(mockEndpoints); + + const result = controller.getChainHealth(); + expect(result).toEqual({ endpoints: mockEndpoints }); + }); + + it("propagates errors from sorobanService.getEndpointHealthReport", () => { + const svc = controller["sorobanService"] as unknown as { getEndpointHealthReport: jest.Mock }; + svc.getEndpointHealthReport = jest.fn().mockImplementation(() => { throw new Error("rpc down"); }); + expect(() => controller.getChainHealth()).toThrow("rpc down"); + }); + }); + + // ------------------------------------------------------------------------- + // getLatestLedger + // ------------------------------------------------------------------------- + + describe("getLatestLedger", () => { + it("calls sorobanService.getLatestLedger and returns its result", async () => { + const mockResult = { id: "ledgerhash", sequence: 9999 }; + mockSorobanService.getLatestLedger.mockResolvedValueOnce(mockResult); + + const result = await controller.getLatestLedger(); + + expect(mockSorobanService.getLatestLedger).toHaveBeenCalledTimes(1); + expect(result).toEqual(mockResult); + }); + + it("propagates errors from sorobanService.getLatestLedger", async () => { + mockSorobanService.getLatestLedger.mockRejectedValueOnce(new Error("ledger unavailable")); + + await expect(controller.getLatestLedger()).rejects.toThrow("ledger unavailable"); + }); + }); + + // ------------------------------------------------------------------------- + // getNetwork + // ------------------------------------------------------------------------- + + describe("getNetwork", () => { + it("calls sorobanService.getNetwork and returns its result", async () => { + const mockResult = { passphrase: "Test SDF Network ; September 2015" }; + mockSorobanService.getNetwork.mockResolvedValueOnce(mockResult); + + const result = await controller.getNetwork(); + + expect(mockSorobanService.getNetwork).toHaveBeenCalledTimes(1); + expect(result).toEqual(mockResult); + }); + + it("propagates errors from sorobanService.getNetwork", async () => { + mockSorobanService.getNetwork.mockRejectedValueOnce(new Error("network unreachable")); + + await expect(controller.getNetwork()).rejects.toThrow("network unreachable"); + }); + }); + + // ------------------------------------------------------------------------- + // getAccount + // ------------------------------------------------------------------------- + + describe("getAccount", () => { + // Real strkeys (valid CRC16). A well-formed-looking "G…" string with a bad + // checksum is rejected by the controller, so fixtures must be genuine. + const PUBLIC_KEY = "GAMS2CGT4CPVYB5LSZV3FAOYFJK67574RS5HASJNTNS7WEUO3CN6ADW4"; + const OTHER_KEY = "GCGMQIBI2B64NO4JI5IRXUOKFQYFUXBRJUUQBOHJQZA34JOKFU3W2WVK"; + + it("passes the publicKey path param through to sorobanService.getAccount", async () => { + const mockAccount = { id: PUBLIC_KEY, sequence: "98765" }; + mockSorobanService.getAccount.mockResolvedValueOnce(mockAccount); + + const result = await controller.getAccount(PUBLIC_KEY); + + expect(mockSorobanService.getAccount).toHaveBeenCalledTimes(1); + expect(mockSorobanService.getAccount).toHaveBeenCalledWith(PUBLIC_KEY); + expect(result).toEqual(mockAccount); + }); + + it("passes a different publicKey correctly", async () => { + const anotherKey = "GBCI24BNYGGIRDE4PCUD6PJAQINUVQPIUJCBJT4HTZZONEXNVVDIYVAC"; + mockSorobanService.getAccount.mockResolvedValueOnce({ id: anotherKey }); + mockSorobanService.getAccount.mockResolvedValueOnce({ id: OTHER_KEY }); + + await controller.getAccount(OTHER_KEY); + + expect(mockSorobanService.getAccount).toHaveBeenCalledWith(OTHER_KEY); + }); + + it("propagates errors from sorobanService.getAccount", async () => { + mockSorobanService.getAccount.mockRejectedValueOnce(new Error("account not found")); + + await expect(controller.getAccount(PUBLIC_KEY)).rejects.toThrow("account not found"); + }); + + it("rejects a strkey-shaped string with an invalid checksum", () => { + // Same length/prefix as a real key, corrupt payload → checksum fails. + const badChecksum = `G${PUBLIC_KEY.slice(1, -1)}A`; + + expect(() => controller.getAccount(badChecksum)).toThrow(BadRequestException); + expect(mockSorobanService.getAccount).not.toHaveBeenCalled(); + }); + + it("rejects a non-G key such as a contract id", () => { + const contractId = "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; + + expect(() => controller.getAccount(contractId)).toThrow(BadRequestException); + expect(mockSorobanService.getAccount).not.toHaveBeenCalled(); + }); + + it("rejects an empty key", () => { + expect(() => controller.getAccount("")).toThrow(BadRequestException); + expect(mockSorobanService.getAccount).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/soroban/soroban.module.ts b/src/soroban/soroban.module.ts index e69de29b..c9b9e863 100644 --- a/src/soroban/soroban.module.ts +++ b/src/soroban/soroban.module.ts @@ -0,0 +1,76 @@ +import { forwardRef, Module } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { EventIngestionService } from "./event-ingestion.service"; +import { ShadowController } from "./shadow.controller"; +import { ShadowService } from "./shadow.service"; +import { SorobanController } from "./soroban.controller"; +import { SorobanService } from "./soroban.service"; +import { SolverRegistryService } from "./solver-registry.service"; +import { SignerService } from "./signer.service"; +import { StellarTxService } from "./stellar-tx.service"; +import { TxConfirmationService } from "./tx-confirmation.service"; +import { SolverRegistryEventsService } from "./events/solver-registry-events.service"; +import { SIGNER_TOKEN, signerFactory } from "./signers/signer.factory"; +import { SolversModule } from "../solvers/solvers.module"; +import { MetricsService } from "../metrics/metrics.service"; +import { AppConfig } from "../config/configuration"; +import { IntentsModule } from "../intents/intents.module"; + +// MetricsModule is @Global() and registered in AppModule, so the MetricsService +// that ShadowService emits its counters through needs no import here. +@Module({ + // SorobanModule <-> SolversModule <-> IntentsModule (which imports this + // module) form a CommonJS cycle, so both sides are wrapped in forwardRef. + // IntentsModule → SorobanModule (IntentsService submits settlement writes) + // and SorobanModule → IntentsModule (EventIngestionService reconciles + // intents from on-chain events). SolversModule supplies SolversService to + // EventIngestionService and, via IntentsModule, also participates in the + // cycle — so it is deferred too. + // module) form a CommonJS cycle. Both edges must be deferred — a bare import + // resolves to `undefined` when the peer module is still mid-initialisation. + // `forwardRef` is required on both sides: EventIngestionService reads an + // Intent back to date its confirmation metric, so SorobanModule needs + // IntentsModule, and IntentsModule already needs ShadowService from here. + // SolversModule supplies SolversService to EventIngestionService and, via + // IntentsModule, also participates in the cycle — so it is deferred too. + imports: [forwardRef(() => IntentsModule), forwardRef(() => SolversModule)], + controllers: [SorobanController, ShadowController], + providers: [ + SorobanService, + + // ── Pluggable signer backend (issue #400) ───────────────────────────── + // Factory selects LocalKeypairSigner (SIGNER_BACKEND=local, default) or + // VaultTransitSigner (SIGNER_BACKEND=vault) at bootstrap. All other + // services inject SignerService and are unaware of the active backend. + { + provide: SIGNER_TOKEN, + inject: [ConfigService, MetricsService], + useFactory: signerFactory, + }, + SignerService, + + // ── On-chain tx pipeline (issue #394) ───────────────────────────────── + TxConfirmationService, + StellarTxService, + + SolverRegistryService, + EventIngestionService, + + // ── Solver-registry event ingestion (issue #399) ────────────────────── + SolverRegistryEventsService, + // Issue #401 — shadow-mode divergence monitor. Exported so IntentsService + // can report off-chain transitions to it without importing Soroban internals. + ShadowService, + ], + exports: [ + SorobanService, + SolverRegistryService, + SignerService, + StellarTxService, + TxConfirmationService, + EventIngestionService, + SolverRegistryEventsService, + ShadowService, + ], +}) +export class SorobanModule {} diff --git a/src/soroban/soroban.service.ts b/src/soroban/soroban.service.ts index e69de29b..f22558a9 100644 --- a/src/soroban/soroban.service.ts +++ b/src/soroban/soroban.service.ts @@ -0,0 +1,103 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { SorobanRpc, Transaction } from "@stellar/stellar-sdk"; +import { AppConfig } from "../config/configuration"; + +@Injectable() +export class SorobanService { + private readonly server: SorobanRpc.Server; + private readonly rpcUrl: string; + + constructor(configService: ConfigService) { + const rpcUrl = configService.get("stellar.sorobanRpcUrl", { infer: true }); + this.rpcUrl = rpcUrl; + this.server = new SorobanRpc.Server(rpcUrl, { allowHttp: rpcUrl.startsWith("http://") }); + } + + getHealth() { + return this.server.getHealth(); + } + + getLatestLedger() { + return this.server.getLatestLedger(); + } + + getNetwork() { + return this.server.getNetwork(); + } + + getAccount(publicKey: string) { + return this.server.getAccount(publicKey); + } + + /** + * Fetch a ledger header by sequence number. + * + * Used by the event-ingestion loop to date the newest event it has seen: the + * `closeTime` here is what makes `vortex_event_ingestion_lag_seconds` a real + * measurement rather than a guess. + * + * @stellar/stellar-sdk 12 has no typed wrapper for the RPC `getLedgers` + * method, so the JSON-RPC call is issued directly. The result is returned in + * the `{ header: { closeTime } }` shape the ingestion loop reads. + */ + async getLedger(sequence: number): Promise<{ header?: { closeTime?: string } }> { + const response = await fetch(this.rpcUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: `get-ledgers-${sequence}`, + method: "getLedgers", + params: { startLedger: sequence, endLedger: sequence }, + }), + }); + if (!response.ok) { + throw new Error(`getLedgers HTTP ${response.status} for ledger ${sequence}`); + } + const body = (await response.json()) as { + result?: { ledgers?: Array<{ ledgerCloseTime?: string }> }; + error?: { message?: string }; + }; + if (body.error) { + throw new Error(`getLedgers RPC error for ledger ${sequence}: ${body.error.message ?? "unknown"}`); + } + const ledger = body.result?.ledgers?.[0]; + return ledger ? { header: { closeTime: ledger.ledgerCloseTime } } : {}; + getLedger(sequence: number) { + // The stellar-sdk 12.x Server type no longer exposes `getLedger`; the call + // is preserved for the event-ingestion lag metric. Cast to keep compiling + // against the pinned SDK — the runtime API may need a follow-up migration. + return (this.server as unknown as { + getLedger(seq: number): Promise<{ header?: { closeTime?: string | number } }>; + }).getLedger(sequence); + } + + getEvents(request: SorobanRpc.Server.GetEventsRequest) { + return this.server.getEvents(request); + } + + getFeeStats(): Promise { + return this.server.getFeeStats(); + } + + simulateTransaction( + transaction: Transaction, + ): Promise { + return this.server.simulateTransaction(transaction); + } + + prepareTransaction( + transaction: Transaction, + ): Promise { + return this.server.prepareTransaction(transaction) as Promise; + } + + submitTransaction(transaction: Transaction): Promise { + return this.server.sendTransaction(transaction); + } + + getTransaction(hash: string): Promise { + return this.server.getTransaction(hash); + } +} diff --git a/src/soroban/stellar-tx.service.spec.ts b/src/soroban/stellar-tx.service.spec.ts index e69de29b..f322b312 100644 --- a/src/soroban/stellar-tx.service.spec.ts +++ b/src/soroban/stellar-tx.service.spec.ts @@ -0,0 +1,591 @@ +import { + Account, + Asset, + BASE_FEE, + Keypair, + nativeToScVal, + Networks, + Operation, + SorobanRpc, + Transaction, + TransactionBuilder, + xdr, +} from "@stellar/stellar-sdk"; +import { ConfigService } from "@nestjs/config"; +import { StellarTxService, type SimulateContractParams } from "./stellar-tx.service"; +import { SorobanService } from "./soroban.service"; +import { SignerService } from "./signer.service"; +import { TxConfirmationService } from "./tx-confirmation.service"; +import { AppConfig } from "../config/configuration"; +import { KillSwitchService } from "../killswitch/killswitch.service"; + +function buildTestTransaction(fee = "100"): Transaction { + const keypair = Keypair.random(); + const account = new Account(keypair.publicKey(), "1"); + return new TransactionBuilder(account, { fee, networkPassphrase: Networks.TESTNET }) + .addOperation(Operation.payment({ destination: keypair.publicKey(), asset: Asset.native(), amount: "1" })) + .setTimeout(30) + .build(); +} + +function feeStats(sorobanInclusionFeeP50: string): SorobanRpc.Api.GetFeeStatsResponse { + const distribution = { + max: sorobanInclusionFeeP50, + min: sorobanInclusionFeeP50, + mode: sorobanInclusionFeeP50, + p10: sorobanInclusionFeeP50, + p20: sorobanInclusionFeeP50, + p30: sorobanInclusionFeeP50, + p40: sorobanInclusionFeeP50, + p50: sorobanInclusionFeeP50, + p60: sorobanInclusionFeeP50, + p70: sorobanInclusionFeeP50, + p80: sorobanInclusionFeeP50, + p90: sorobanInclusionFeeP50, + p95: sorobanInclusionFeeP50, + p99: sorobanInclusionFeeP50, + transactionCount: "1", + ledgerCount: 1, + }; + return { sorobanInclusionFee: distribution, inclusionFee: distribution, latestLedger: 1 }; +} + +function simulationSuccess(minResourceFee: string): SorobanRpc.Api.SimulateTransactionSuccessResponse { + return { + id: "1", + latestLedger: 1, + events: [], + _parsed: true, + minResourceFee, + transactionData: {} as SorobanRpc.Api.SimulateTransactionSuccessResponse["transactionData"], + cost: { cpuInsns: "0", memBytes: "0" }, + }; +} + +function simulationError(message: string): SorobanRpc.Api.SimulateTransactionErrorResponse { + return { id: "1", error: message, latestLedger: 1, events: [], _parsed: true }; +} + +/** A syntactically valid contract id (`Address.fromString` must accept it). */ +const CONTRACT_ID = "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; + +/** + * A SignerService stub that satisfies the members StellarTxService actually + * calls: `getPublicKey`, `withNextSequence` and `sign`. A bare `{}` double + * makes those calls throw `is not a function` and masks the real assertion. + */ +function stubSignerService(publicKey = Keypair.random().publicKey()): SignerService { + return { + getPublicKey: () => publicKey, + getNetworkPassphrase: () => Networks.TESTNET, + withNextSequence: (fn: (sequence: string) => Promise) => fn("0"), + sign: async (tx: T) => tx, + } as unknown as SignerService; +} + +/** TxConfirmationService stub — confirmation is never really awaited here. */ +function stubConfirmationService(): TxConfirmationService { + return { + confirm: async () => ({ hash: "stub-hash", status: "SUCCESS", durationMs: 0 }), + waitForConfirmation: async () => ({ hash: "stub-hash", status: "SUCCESS", durationMs: 0 }), + } as unknown as TxConfirmationService; +} + +/** Width of a transaction's ledger validity window, in seconds. */ +function validityWindowSeconds(transaction: Transaction): number { + const bounds = transaction.timeBounds; + if (!bounds) throw new Error("expected the simulation envelope to carry a validity window"); + // `TransactionBuilder.setTimeout(n)` does not store a width: it writes + // `{ minTime: 0, maxTime: + n }`, leaving the lower bound + // at 0 for the ledger to read as "now". Resolving that 0 against the wall + // clock is what turns the pair back into the width the caller asked for — + // subtracting 0 - 0 would report an epoch timestamp instead. + const lowerBound = Number(bounds.minTime) || Math.floor(Date.now() / 1000); + return Number(bounds.maxTime) - lowerBound; +} + +/** + * Pin `Date.now()` so the `setTimeout`-derived `maxTime` in the simulation + * envelope is reproducible and the window width above is exact rather than + * ±1 s depending on where in the second the assertion runs. + * + * Only `Date.now` is stubbed; real timers, promises and I/O are untouched. + */ +function freezeClock(ms = Date.UTC(2026, 0, 1)): void { + jest.spyOn(Date, "now").mockReturnValue(ms); +} + +describe("StellarTxService", () => { + let sorobanService: jest.Mocked>; + let configService: jest.Mocked, "get">>; + let killSwitch: { evaluateTarget: jest.Mock }; + let service: StellarTxService; + + /** Default: no pause active, so pre-existing behaviour is unchanged. */ + const notPaused = { paused: false, matched: null, matchedChain: [] }; + + /** + * `signerService` and `confirmationService` are only touched on the live + * submit path; the fee/dry-run/simulation paths exercised here never reach + * them, so empty stand-ins keep the constructor honest about its arity. + */ + const unusedSigner = {} as unknown as SignerService; + const unusedConfirmation = {} as unknown as TxConfirmationService; + + beforeEach(() => { + sorobanService = { + getFeeStats: jest.fn(), + simulateTransaction: jest.fn(), + prepareTransaction: jest.fn(), + }; + configService = { get: jest.fn().mockReturnValue("p50") }; + killSwitch = { evaluateTarget: jest.fn().mockReturnValue(notPaused) }; + service = new StellarTxService( + sorobanService as unknown as SorobanService, + unusedSigner, + unusedConfirmation, + stubSignerService(), + stubConfirmationService(), + configService as unknown as ConfigService, + killSwitch as unknown as KillSwitchService, + ); + }); + + describe("estimateBaseFee", () => { + it("returns the configured fee percentile from network fee stats", async () => { + sorobanService.getFeeStats.mockResolvedValue(feeStats("250")); + + await expect(service.estimateBaseFee()).resolves.toBe("250"); + }); + + it("falls back to BASE_FEE when the reported fee is 0", async () => { + sorobanService.getFeeStats.mockResolvedValue(feeStats("0")); + + await expect(service.estimateBaseFee()).resolves.toBe(BASE_FEE); + }); + + it("falls back to BASE_FEE when fee stats are unavailable", async () => { + sorobanService.getFeeStats.mockRejectedValue(new Error("rpc unavailable")); + + await expect(service.estimateBaseFee()).resolves.toBe(BASE_FEE); + }); + }); + + describe("estimateFee", () => { + it("combines the network base fee with the simulated resource fee", async () => { + sorobanService.getFeeStats.mockResolvedValue(feeStats("300")); + sorobanService.simulateTransaction.mockResolvedValue(simulationSuccess("45000")); + + const estimate = await service.estimateFee(buildTestTransaction()); + + expect(estimate).toEqual({ baseFee: "300", resourceFee: "45000", totalFee: "45300" }); + }); + + it("throws when simulation fails, without marking anything as prepared", async () => { + sorobanService.getFeeStats.mockResolvedValue(feeStats("300")); + sorobanService.simulateTransaction.mockResolvedValue(simulationError("boom")); + + await expect(service.estimateFee(buildTestTransaction())).rejects.toThrow(/simulation error: boom/); + expect(sorobanService.prepareTransaction).not.toHaveBeenCalled(); + }); + }); + + describe("prepareTransaction", () => { + it("submits the transaction with the estimated base fee applied", async () => { + sorobanService.getFeeStats.mockResolvedValue(feeStats("300")); + const prepared = buildTestTransaction("45300"); + sorobanService.prepareTransaction.mockResolvedValue(prepared); + + const result = await service.prepareTransaction(buildTestTransaction()); + + expect(result).toBe(prepared); + const [submittedTx] = sorobanService.prepareTransaction.mock.calls[0]; + expect((submittedTx as Transaction).fee).toBe("300"); + }); + }); + + describe("invokeContract — dry-run mode (#260)", () => { + it("returns dryRun:true without calling any soroban method when dryRun=true", async () => { + // configService returns dryRun=true for onchainDryRun + const dryRunConfigService = { + get: jest.fn((key: string) => { + if (key === "stellar.feePercentile") return "p50"; + if (key === "onchainDryRun") return true; + return undefined; + }), + } as unknown as ConfigService; + + const dryRunService = new StellarTxService( + sorobanService as unknown as SorobanService, + unusedSigner, + unusedConfirmation, + stubSignerService(), + stubConfirmationService(), + dryRunConfigService, + killSwitch as unknown as KillSwitchService, + ); + + const result = await dryRunService.invokeContract({ + contractId: "CTEST", + method: "create_intent", + args: [], + }); + + expect(result.dryRun).toBe(true); + expect(result.status).toBe("DRY_RUN"); + // No network calls should be made in dry-run mode + expect(sorobanService.simulateTransaction).not.toHaveBeenCalled(); + expect(sorobanService.prepareTransaction).not.toHaveBeenCalled(); + }); + + it("signs, submits and confirms when dryRun=false (live path)", async () => { + // NOTE: this test used to assert the live path threw "not yet + // implemented". That branch is gone — `invokeContract` now runs the full + // simulate → prepare → sign → submit → confirm pipeline, so the + // assertion is stated against what the service actually does today. + const liveConfigService = { + get: jest.fn((key: string) => { + if (key === "stellar.feePercentile") return "p50"; + if (key === "onchainDryRun") return false; + return undefined; + }), + } as unknown as ConfigService; + + const liveSoroban = { + getFeeStats: jest.fn().mockResolvedValue(feeStats("100")), + simulateTransaction: jest.fn().mockResolvedValue(simulationSuccess("45000")), + prepareTransaction: jest.fn().mockImplementation(async (tx: Transaction) => tx), + submitTransaction: jest.fn().mockResolvedValue({ hash: "live-hash", status: "SUCCESS" }), + }; + const confirmation = { + waitForConfirmation: jest + .fn() + .mockResolvedValue({ hash: "live-hash", status: "SUCCESS", durationMs: 12 }), + }; + + const liveService = new StellarTxService( + sorobanService as unknown as SorobanService, + { + // The live path hands the envelope to the signer; this suite only + // asserts the dry-run gate releases control to it. + withNextSequence: jest.fn().mockRejectedValue(new Error("not yet implemented")), + } as unknown as SignerService, + unusedConfirmation, + liveSoroban as unknown as SorobanService, + stubSignerService(), + confirmation as unknown as TxConfirmationService, + liveConfigService, + killSwitch as unknown as KillSwitchService, + ); + + // A real contract id: the SDK rejects a placeholder in `new Contract(..)` + // before the live branch ever gets to do anything meaningful. + await expect( + liveService.invokeContract({ + contractId: CONTRACT_ID, + method: "create_intent", + args: [], + }), + ).resolves.toEqual({ hash: "live-hash", status: "SUCCESS", dryRun: false, restored: false }); + + expect(liveSoroban.submitTransaction).toHaveBeenCalledTimes(1); + expect(confirmation.waitForConfirmation).toHaveBeenCalledTimes(1); + }); + }); + + describe("simulateContract (#401 read-only shadow primitive)", () => { + const sourceKeypair = Keypair.random(); + + // `freezeClock()` is opt-in per test; always drop the `Date.now` stub. + afterEach(() => { + jest.restoreAllMocks(); + }); + + type SimulateDeps = jest.Mocked< + Pick< + SorobanService, + "getFeeStats" | "simulateTransaction" | "getAccount" | "getLatestLedger" | "prepareTransaction" | "submitTransaction" + > + >; + + function buildShadowService( + config: { + queueMax?: unknown; + concurrency?: unknown; + onchainDryRun?: boolean; + } = {}, + ): { service: StellarTxService; soroban: SimulateDeps } { + const soroban: SimulateDeps = { + getFeeStats: jest.fn().mockResolvedValue(feeStats("100")), + simulateTransaction: jest.fn(), + getAccount: jest.fn().mockResolvedValue(new Account(sourceKeypair.publicKey(), "42")), + getLatestLedger: jest.fn().mockResolvedValue({ id: "1", sequence: "500" }), + prepareTransaction: jest.fn(), + submitTransaction: jest.fn(), + }; + + const configService = { + get: jest.fn((key: string) => { + if (key === "stellar.feePercentile") return "p50"; + if (key === "stellar.network") return "testnet"; + if (key === "onchainDryRun") return config.onchainDryRun ?? true; + if (key === "shadow.queueMax") return config.queueMax; + if (key === "shadow.concurrency") return config.concurrency; + return undefined; + }), + } as unknown as ConfigService; + + return { + service: new StellarTxService( + soroban as unknown as SorobanService, + unusedSigner, + unusedConfirmation, + configService, + { evaluateTarget: () => notPaused } as unknown as KillSwitchService, + stubSignerService(), + stubConfirmationService(), + configService, + {} as unknown as KillSwitchService, + ), + soroban, + }; + } + + function params(overrides: Partial = {}): SimulateContractParams { + return { + contractId: CONTRACT_ID, + method: "accept_intent", + args: [nativeToScVal("intent-1", { type: "string" })], + sourceAccount: sourceKeypair.publicKey(), + ...overrides, + }; + } + + it("reports ok when the contract would have accepted the call", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("45000")); + + await expect(service.simulateContract(params())).resolves.toEqual({ outcome: "ok" }); + }); + + it("never prepares or submits anything — the envelope is unsigned and unbroadcast", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("45000")); + + await service.simulateContract(params()); + + expect(soroban.simulateTransaction).toHaveBeenCalledTimes(1); + expect(soroban.prepareTransaction).not.toHaveBeenCalled(); + expect(soroban.submitTransaction).not.toHaveBeenCalled(); + }); + + it("simulates even with ONCHAIN_DRY_RUN unset, because dry-run governs broadcast", async () => { + const { service, soroban } = buildShadowService({ onchainDryRun: false }); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("45000")); + + await expect(service.simulateContract(params())).resolves.toEqual({ outcome: "ok" }); + }); + + it("skips rather than guesses when no source account is configured", async () => { + const { service, soroban } = buildShadowService(); + + const result = await service.simulateContract(params({ sourceAccount: " " })); + + expect(result.outcome).toBe("skipped"); + expect(result.detail).toMatch(/source account/i); + expect(soroban.simulateTransaction).not.toHaveBeenCalled(); + }); + + it("skips when no settlement contract is configured", async () => { + const { service, soroban } = buildShadowService(); + + const result = await service.simulateContract(params({ contractId: "" })); + + expect(result.outcome).toBe("skipped"); + expect(result.detail).toMatch(/contract/i); + expect(soroban.simulateTransaction).not.toHaveBeenCalled(); + }); + + it("classifies a contract guard failure as a rejection, not an error", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue( + simulationError("HostError: Error(Contract, #1) insufficient balance"), + ); + + const result = await service.simulateContract(params()); + + expect(result.outcome).toBe("rejected"); + expect(result.detail).toContain("insufficient balance"); + }); + + it("classifies a hard failure as a contract error", async () => { + const { service, soroban } = buildShadowService(); + // Deliberately carries none of the revert markers the classifier keys on + // ("error(contract", "error(wasmvm", "revert"): the host reached the + // contract and it failed outright rather than refusing the call. + soroban.simulateTransaction.mockResolvedValue( + simulationError("HostError: Error(Context, InvalidAction) missing export"), + ); + + const result = await service.simulateContract(params()); + + expect(result.outcome).toBe("error"); + expect(result.detail).toContain("missing export"); + }); + + it("reports an unreachable RPC as unavailable, not as a contract failure", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockRejectedValue(new Error("connect ECONNREFUSED")); + + const result = await service.simulateContract(params()); + + expect(result.outcome).toBe("unavailable"); + expect(result.detail).toContain("ECONNREFUSED"); + }); + + it("reports an empty response as unavailable", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue( + undefined as unknown as SorobanRpc.Api.SimulateTransactionResponse, + ); + + await expect(service.simulateContract(params())).resolves.toMatchObject({ + outcome: "unavailable", + }); + }); + + it("reports an unbuildable envelope as unavailable without asking the contract", async () => { + const { service, soroban } = buildShadowService(); + + const result = await service.simulateContract(params({ contractId: "not-a-contract-id" })); + + expect(result.outcome).toBe("unavailable"); + expect(result.detail).toMatch(/could not build/i); + expect(soroban.simulateTransaction).not.toHaveBeenCalled(); + }); + + it("uses the source account's real sequence number when it is on chain", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + + await service.simulateContract(params()); + + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + expect((submitted as Transaction).sequence).toBe("42"); + // The account is at 42, and a Stellar transaction must carry the *next* + // sequence to use, so the SDK's TransactionBuilder bumps it by one. + expect((submitted as Transaction).sequence).toBe("43"); + expect(soroban.getLatestLedger).not.toHaveBeenCalled(); + }); + + it("falls back to the latest ledger for a source account that has never been on chain", async () => { + const { service, soroban } = buildShadowService(); + soroban.getAccount.mockRejectedValue(new Error("not found")); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + + await service.simulateContract(params()); + + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + // 500 (latest closed) + 1: the next sequence the account would hold. + expect((submitted as Transaction).sequence).toBe("501"); + // Ledger 500 → the service offers 501 as the account's sequence, and the + // builder adds the transaction-level +1 on top. + expect((submitted as Transaction).sequence).toBe("502"); + }); + + it("falls back to sequence 0 when neither the account nor the ledger can be read", async () => { + const { service, soroban } = buildShadowService(); + soroban.getAccount.mockRejectedValue(new Error("not found")); + soroban.getLatestLedger.mockRejectedValue(new Error("rpc down")); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + + const result = await service.simulateContract(params()); + + expect(result.outcome).toBe("ok"); + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + expect((submitted as Transaction).sequence).toBe("0"); + // Base sequence 0, plus the builder's transaction-level +1. + expect((submitted as Transaction).sequence).toBe("1"); + }); + + // Regression guard for a real defect that was fixed: the service used to + // build its operation with the stellar-sdk 11 shape + // Operation.invokeHostFunction({ func: xdr.HostFunctionType.hostFunctionTypeInvokeContract, args: [...] }) + // which stellar-sdk 12.x does not accept — `func` must be a constructed + // `xdr.HostFunction` and `args` is not a top-level option at all. The + // resulting envelope could not be XDR-encoded ("() => inst has union name + // undefined, not HostFunction"), so every shadow simulation the monitor put + // on the wire was malformed. The `toXDR()` assertion below is what proves + // the envelope is now structurally valid. + it("builds a single, well-formed host-function operation for the named method", async () => { + const { service, soroban } = buildShadowService(); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + + const result = await service.simulateContract(params({ method: "fill_intent" })); + + expect(result.outcome).toBe("ok"); + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + const tx = submitted as Transaction; + expect(tx.operations).toHaveLength(1); + const envelope = (submitted as Transaction).toEnvelope(); + expect((envelope.value() as xdr.TransactionV1Envelope).tx().operations()).toHaveLength(1); + // Round-trips through XDR, so the host function and every ScVal the + // monitor built are structurally valid — which is the whole reason the + // monitor cannot blame a malformed envelope for a "divergence". + expect(() => tx.toXDR()).not.toThrow(); + }); + + it("sizes the ledger validity window from the worst-case shadow queue drain", async () => { + // 1000 queued at 4-way concurrency = 250 sequential batches; at an + // assumed 3 s per simulation that is 750 s, plus 60 s of slack. + const { service, soroban } = buildShadowService({ queueMax: 1000, concurrency: 4 }); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + freezeClock(); + + await service.simulateContract(params()); + + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + expect(validityWindowSeconds(submitted as Transaction)).toBe(810); + }); + + it("clamps the validity window to a ledger-acceptable range", async () => { + // Floor: 256 queued at 4-way concurrency is well under a minute of + // drain, so the 300 s minimum applies. + const small = buildShadowService({ queueMax: 256, concurrency: 4 }); + small.soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + freezeClock(); + await small.service.simulateContract(params()); + const [smallTx] = small.soroban.simulateTransaction.mock.calls[0]; + expect(validityWindowSeconds(smallTx as Transaction)).toBe(300); + + // Ceiling: an absurd queue must not produce an absurd window. + const huge = buildShadowService({ queueMax: 1_000_000, concurrency: 1 }); + huge.soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + freezeClock(); + await huge.service.simulateContract(params()); + const [hugeTx] = huge.soroban.simulateTransaction.mock.calls[0]; + expect(validityWindowSeconds(hugeTx as Transaction)).toBe(3600); + }); + + it("still builds a valid envelope when the queue settings are unparseable", async () => { + const { service, soroban } = buildShadowService({ queueMax: "many", concurrency: NaN }); + soroban.simulateTransaction.mockResolvedValue(simulationSuccess("1")); + freezeClock(); + + const result = await service.simulateContract(params()); + + expect(result.outcome).toBe("ok"); + const [submitted] = soroban.simulateTransaction.mock.calls[0]; + expect(validityWindowSeconds(submitted as Transaction)).toBe(300); + }); + + it("never throws, whatever the RPC does", async () => { + const { service, soroban } = buildShadowService(); + soroban.getFeeStats.mockRejectedValue(new Error("fee stats down")); + + await expect(service.simulateContract(params())).resolves.toMatchObject({ + outcome: "unavailable", + }); + }); + }); +}); diff --git a/src/soroban/stellar-tx.service.ts b/src/soroban/stellar-tx.service.ts index e69de29b..36eaadbc 100644 --- a/src/soroban/stellar-tx.service.ts +++ b/src/soroban/stellar-tx.service.ts @@ -0,0 +1,708 @@ +/** + * StellarTxService (issue #394 — archived contract state) + * ───────────────────────────────────────────────────────── + * Builds, simulates, and submits Soroban contract invocations. + * + * Preflight pipeline (issue #394) + * ──────────────────────────────── + * Soroban state archival means persistent ledger entries (intents, solver + * bonds) whose TTL lapsed become archived. Any invocation that touches them + * will fail simulation with a `restorePreamble` — a block that describes the + * entries that must be restored before the call can succeed. + * + * StellarTxService now detects this condition and automatically: + * 1. Builds a RestoreFootprint transaction from the preamble. + * 2. Signs, submits, and confirms the restore transaction. + * 3. Re-simulates the original transaction on the freshly-restored state. + * 4. Submits the original transaction. + * + * A max-one-restore guard prevents infinite loops: if the re-simulation still + * yields a restorePreamble, the call fails with a clear error. + * + * Constraints (from the issue): + * • Restore fees respect the configured fee ceiling (same percentile-based + * estimation as regular Soroban fees). + * • ONCHAIN_DRY_RUN=true suppresses all on-chain writes (restore included). + * • Restore count and fee are recorded in Prometheus metrics. + */ + +import { Injectable, Logger, Optional } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { + Account, + BASE_FEE, + Contract, + FeeBumpTransaction, + Operation, + SorobanDataBuilder, + Networks, + SorobanRpc, + Transaction, + TransactionBuilder, + xdr, +} from "@stellar/stellar-sdk"; +import { AppConfig, FeePercentile, NETWORK_PASSPHRASES } from "../config/configuration"; +import { classifySimulationResponse } from "./shadow-divergence"; +import { SorobanService } from "./soroban.service"; +import { SignerService } from "./signer.service"; +import { FeatureFlagService } from "../flags/feature-flag.service"; +import { TxConfirmationService } from "./tx-confirmation.service"; +import { MetricsService } from "../metrics/metrics.service"; +import { KillSwitchService } from "../killswitch/killswitch.service"; +import { + assertNotPaused, + KillSwitchActiveException, +} from "../killswitch/killswitch.guard"; +import { STELLAR_CHAIN } from "../intents/intents.types"; + +/** + * Assumed per-simulation RPC latency, used to size the envelope's ledger + * validity window. + * + * The window has to outlast the *whole* queue, not one item: a simulation + * enqueued behind `queueMax / concurrency` slow RPCs must not have expired by + * the time it is asked, or it comes back as a divergence and inflates the very + * ratio the cutover runbook reads as a pass. + */ +const ASSUMED_SIMULATION_RPC_MS = 3_000; + +/** Floor for the validity window, in seconds. */ +const MIN_SIMULATION_TIMEOUT_SECONDS = 300; + +/** Ceiling for the validity window, in seconds (Stellar rejects absurd values). */ +const MAX_SIMULATION_TIMEOUT_SECONDS = 3_600; + +export interface FeeEstimate { + /** Classic inclusion fee, in stroops. */ + baseFee: string; + /** Soroban resource fee returned by simulation, in stroops. */ + resourceFee: string; + /** baseFee + resourceFee, in stroops. */ + totalFee: string; +} + +export interface InvokeContractParams { + contractId: string; + method: string; + args: xdr.ScVal[]; +} + +/** + * Time bound (seconds) on every transaction built by {@link StellarTxService.invokeContract}. + * After this the network rejects the envelope, which is what lets the outbox + * relay treat a NOT_FOUND envelope hash as "never landed, safe to rebuild" + * once its processing lease (OUTBOX_LEASE_SECONDS) has expired (issue #396). + */ +export const INVOKE_TX_TIMEOUT_SECONDS = 30; + +export interface InvokeContractOptions { + /** + * Called with the signed envelope's hash after signing and *before* + * broadcast (issue #396). If it throws, nothing is submitted. The outbox + * relay uses this to durably record the hash so a crash mid-submit can be + * detected on retry instead of double-submitting. + */ + beforeSubmit?: (envelopeHash: string) => Promise; +} + +export interface InvokeContractResult { + hash: string; + status: string; + /** + * True when the invocation was simulated only (dry-run mode). + * The hash field contains a placeholder — no transaction was broadcast. + */ + dryRun: boolean; + /** True when a RestoreFootprint transaction was submitted before the main tx. */ + restored?: boolean; +} + +/** Parameters for a read-only, never-broadcast contract simulation. */ +export interface SimulateContractParams { + contractId: string; + method: string; + args: xdr.ScVal[]; + /** + * Public key used as the transaction source for the simulation. + * + * The key is used only to satisfy the envelope's source-account field; the + * envelope is never signed and never submitted, so the key needs no balance, + * no sequence of its own and is never charged a fee. Leaving it empty + * short-circuits to a "cannot simulate" result rather than guessing. + */ + sourceAccount?: string; +} + +/** + * Verdict from a simulated contract call. + * + * - `ok` — the contract would have accepted the call. + * - `rejected` — the contract was reached and refused it (a `require!` guard + * tripped, an invariant failed, the method refused the state transition). + * - `error` — the contract was reached and failed: the call was made and the + * contract did not complete it. This is a statement about the contract. + * - `unavailable` — no verdict was obtained at all: the RPC was unreachable, + * the envelope could not be built, or the response was empty. This is a + * statement about us, and the monitor reports it as `simulation_exception` + * rather than blaming the contract for our outage. + * - `skipped` — the simulation was not attempted (no source account / contract + * configured). Never reported as agreement. + */ +export type SimulateContractOutcome = "ok" | "rejected" | "error" | "unavailable" | "skipped"; + +export interface SimulateContractResult { + outcome: SimulateContractOutcome; + /** Log-safe explanation. Never contains keys or raw XDR. */ + detail?: string; +} + + +@Injectable() +export class StellarTxService { + private readonly logger = new Logger(StellarTxService.name); + private readonly feePercentile: FeePercentile; + private readonly dryRun: boolean; + private readonly networkPassphrase: string; + /** + * Ledger validity window for simulation envelopes, in seconds. + * + * Sized from the shadow queue's worst-case drain time so an observation that + * waited at the back of the queue still simulates against valid ledger state. + */ + private readonly simulationTimeoutSeconds: number; + + constructor( + private readonly sorobanService: SorobanService, + private readonly signerService: SignerService, + private readonly confirmationService: TxConfirmationService, + configService: ConfigService, + private readonly killSwitch: KillSwitchService, + @Optional() private readonly metricsService?: MetricsService, + @Optional() private readonly flags?: FeatureFlagService, + ) { + this.feePercentile = configService.get("stellar.feePercentile", { infer: true }); + this.dryRun = configService.get("onchainDryRun", { infer: true }); + this.networkPassphrase = + NETWORK_PASSPHRASES[configService.get("stellar.network", { infer: true })] ?? + Networks.TESTNET; + + // NaN-safe on purpose: a missing or unparseable queue setting must not + // propagate into the ledger validity window, where it would produce a + // transaction that cannot be encoded at all. + const configuredQueueMax = Number(configService.get("shadow.queueMax", { infer: true })); + const queueMax = + Number.isFinite(configuredQueueMax) && configuredQueueMax > 0 ? configuredQueueMax : 256; + const configuredConcurrency = Number(configService.get("shadow.concurrency", { infer: true })); + const concurrency = + Number.isFinite(configuredConcurrency) && configuredConcurrency > 0 + ? Math.floor(configuredConcurrency) + : 4; + const batchesToDrain = Math.ceil(queueMax / concurrency); + this.simulationTimeoutSeconds = Math.min( + MAX_SIMULATION_TIMEOUT_SECONDS, + Math.max( + MIN_SIMULATION_TIMEOUT_SECONDS, + Math.ceil((batchesToDrain * ASSUMED_SIMULATION_RPC_MS) / 1000) + 60, + ), + ); + } + + /** + * Recommended classic inclusion fee based on recent network activity. + * Falls back to the network's minimum base fee if fee stats are unavailable + * or the reported fee is degenerate (e.g. an idle network reporting "0"). + */ + async estimateBaseFee(): Promise { + try { + const stats = await this.sorobanService.getFeeStats(); + const fee = stats.sorobanInclusionFee[this.feePercentile]; + return fee && fee !== "0" ? fee : BASE_FEE; + } catch (err) { + this.logger.warn( + `Failed to fetch Soroban fee stats, falling back to base fee ${BASE_FEE}: ${(err as Error).message}`, + ); + return BASE_FEE; + } + } + + /** + * Estimates the total fee (base + resource) required to submit `transaction` + * by simulating it against the network. + */ + async estimateFee(transaction: Transaction): Promise { + const baseFee = await this.estimateBaseFee(); + const simulation = await this.sorobanService.simulateTransaction( + this.withFee(transaction, baseFee), + ); + + if (SorobanRpc.Api.isSimulationError(simulation)) { + throw new Error( + `Fee estimation failed: transaction simulation error: ${simulation.error}`, + ); + } + + const resourceFee = (simulation as SorobanRpc.Api.SimulateTransactionSuccessResponse) + .minResourceFee; + const totalFee = (BigInt(baseFee) + BigInt(resourceFee)).toString(); + + return { baseFee, resourceFee, totalFee }; + } + + /** + * Simulates `transaction` and returns it assembled with the estimated + * base + resource fee and Soroban transaction data, ready to sign. + * + * Detects `restorePreamble` and surfaces it for callers that need to handle + * archival before proceeding (used by `invokeContract`'s preflight pipeline). + */ + async prepareTransaction(transaction: Transaction): Promise { + const baseFee = await this.estimateBaseFee(); + const prepared = await this.sorobanService.prepareTransaction( + this.withFee(transaction, baseFee), + ); + + this.logger.log( + `Prepared transaction with fee ${prepared.fee} stroops (base fee ${baseFee})`, + ); + + return prepared as Transaction; + } + + /** + * Invokes a Soroban contract method with automatic RestoreFootprint preflight. + * + * Dry-run path (ONCHAIN_DRY_RUN=true, the default outside production): + * Simulates the transaction and returns `{ dryRun: true }` — no funds move. + * + * Live path (ONCHAIN_DRY_RUN=false): + * 1. Simulate the transaction. + * 2. If simulation returns a restorePreamble, submit a RestoreFootprint + * transaction first (issue #394), confirm it, then re-simulate. + * 3. Sign and submit the (now-prepared) original transaction. + * 4. Confirm and return the result. + */ + async invokeContract( + params: InvokeContractParams, + options: InvokeContractOptions = {}, + ): Promise { + // Issue #477 — the last gate before anything touches the chain. Checking + // here rather than only in controllers also covers background callers (the + // sweeper, event ingestion) that never pass through an HTTP guard. + // + // Evaluated before the dry-run branch so a pause is visible in logs even + // while on-chain writes are simulated. + this.assertOnChainWriteAllowed(params.method); + + // ONCHAIN_DRY_RUN is the default; the `onchain-dry-run` runtime flag + // (issue #495) can override it without a restart. + const dryRun = this.flags + ? await this.flags.getBooleanValue("onchain-dry-run", { chain: "stellar" }) + : this.dryRun; + if (dryRun) { + this.logger.log( + `[dry-run] invokeContract contractId=${params.contractId} method=${params.method} ` + + `— simulating only, ONCHAIN_DRY_RUN=true (no transaction submitted)`, + ); + return { hash: "dry-run-no-hash", status: "DRY_RUN", dryRun: true }; + } + + this.logger.log( + `invokeContract contractId=${params.contractId} method=${params.method}`, + ); + + return this.signerService.withNextSequence(async (sequence) => { + const account = new Account(this.signerService.getPublicKey(), sequence); + const baseFee = await this.estimateBaseFee(); + + const rawTx = new TransactionBuilder(account, { + fee: baseFee, + networkPassphrase: this.signerService.getNetworkPassphrase(), + }) + .addOperation( + new Contract(params.contractId).call(params.method, ...params.args), + ) + .setTimeout(INVOKE_TX_TIMEOUT_SECONDS) + .build(); + let simulation = await this.sorobanService.simulateTransaction(rawTx); + + let restored = false; + if (this.hasRestorePreamble(simulation)) { + this.logger.warn( + `[stellar-tx] restorePreamble detected for method=${params.method} on contract=${params.contractId}; submitting RestoreFootprint`, + ); + await this.submitRestoreFootprint(simulation, account, baseFee); + restored = true; + + // Re-simulate after restore (max one restore per invocation). + simulation = await this.sorobanService.simulateTransaction(rawTx); + if (this.hasRestorePreamble(simulation)) { + throw new Error( + `invokeContract: restorePreamble still present after restore — aborting to prevent loop (method=${params.method})`, + ); + } + } + + if (SorobanRpc.Api.isSimulationError(simulation)) { + throw new Error( + `invokeContract simulation failed: ${(simulation as SorobanRpc.Api.SimulateTransactionErrorResponse).error}`, + ); + } + + // Assemble with Soroban data + fee. + const prepared = await this.sorobanService.prepareTransaction(rawTx); + const signed = await this.signerService.sign(prepared as Transaction); + + await options.beforeSubmit?.(signed.hash().toString("hex")); + + const submittedAt = Date.now(); + const sendResponse = await this.sorobanService.submitTransaction(signed); + + if (sendResponse.status === "ERROR") { + throw new Error( + `invokeContract submit failed: ${(sendResponse as { errorResultXdr?: string }).errorResultXdr ?? "unknown error"}`, + ); + } + + const confirmation = await this.confirmationService.waitForConfirmation( + sendResponse.hash, + submittedAt, + ); + + if (confirmation.status === "FAILED" || confirmation.status === "TIMEOUT") { + throw new Error( + `invokeContract transaction did not confirm: status=${confirmation.status} error=${confirmation.error}`, + ); + } + + this.logger.log( + `invokeContract succeeded: hash=${sendResponse.hash} method=${params.method} restored=${restored}`, + ); + + return { + hash: sendResponse.hash, + status: "SUCCESS", + dryRun: false, + restored, + }; + }); + } + + // ── RestoreFootprint helpers (issue #394) ────────────────────────────────── + + /** + * Returns true when a simulation response contains a `restorePreamble` + * indicating that one or more ledger entries need to be restored before + * the invocation can proceed. + */ + private hasRestorePreamble( + simulation: SorobanRpc.Api.SimulateTransactionResponse, + ): boolean { + if (SorobanRpc.Api.isSimulationError(simulation)) return false; + const success = simulation as SorobanRpc.Api.SimulateTransactionSuccessResponse & { + restorePreamble?: { minResourceFee: string; transactionData: string }; + }; + return ( + success.restorePreamble !== undefined && + success.restorePreamble.minResourceFee !== undefined + ); + } + + /** + * Build, sign, submit, and confirm a RestoreFootprint transaction using the + * footprint described in `simulation.restorePreamble`. + * + * Fee is estimated from the preamble's `minResourceFee` plus the base + * inclusion fee — respecting the same fee ceiling as normal Soroban ops. + * + * @throws if submission or confirmation fails. + */ + private async submitRestoreFootprint( + simulation: SorobanRpc.Api.SimulateTransactionResponse, + account: Account, + baseFee: string, + ): Promise { + const success = simulation as SorobanRpc.Api.SimulateTransactionSuccessResponse & { + restorePreamble: { minResourceFee: string; transactionData: string }; + }; + + const preamble = success.restorePreamble; + const resourceFee = preamble.minResourceFee; + const totalFee = (BigInt(baseFee) + BigInt(resourceFee)).toString(); + + // Parse the footprint XDR from the preamble. + const sorobanData = SorobanDataBuilder.fromXDR(preamble.transactionData); + + const restoreTx = new TransactionBuilder(account, { + fee: totalFee, + networkPassphrase: this.signerService.getNetworkPassphrase(), + }) + .addOperation(Operation.restoreFootprint({})) + .setSorobanData(sorobanData.build()) + .setTimeout(30) + .build(); + + const signedRestore = await this.signerService.sign(restoreTx); + const submittedAt = Date.now(); + const sendResponse = await this.sorobanService.submitTransaction(signedRestore); + + if (sendResponse.status === "ERROR") { + try { this.metricsService?.incSorobanRestore("failed"); } catch { /* noop */ } + throw new Error( + `RestoreFootprint submission failed: ${(sendResponse as { errorResultXdr?: string }).errorResultXdr ?? "unknown"}`, + ); + } + + const confirmation = await this.confirmationService.waitForConfirmation( + sendResponse.hash, + submittedAt, + ); + + if (confirmation.status !== "SUCCESS") { + try { this.metricsService?.incSorobanRestore("failed"); } catch { /* noop */ } + throw new Error( + `RestoreFootprint did not confirm: status=${confirmation.status} error=${confirmation.error}`, + ); + } + + this.logger.log( + `[stellar-tx] RestoreFootprint confirmed: hash=${sendResponse.hash} ` + + `resourceFee=${resourceFee} durationMs=${confirmation.durationMs}`, + ); + + try { + this.metricsService?.incSorobanRestore("success"); + this.metricsService?.observeRestoreFee(Number(totalFee)); + } catch { /* noop */ } + } + + private withFee(transaction: Transaction | FeeBumpTransaction, fee: string): Transaction { + if ("innerTransaction" in transaction) { + throw new TypeError("fee bump transactions are not supported"); + } + + return TransactionBuilder.cloneFrom(transaction, { + fee, + networkPassphrase: transaction.networkPassphrase, + }).build(); + } + + /** + * Throws when an emergency pause covers this on-chain write. + * + * `onchain` is evaluated rather than the caller's nominal operation, because + * this is the single point every chain write funnels through — pausing + * `onchain` must stop all of them, whichever method they use. + */ + private assertOnChainWriteAllowed(method: string): void { + try { + assertNotPaused(this.killSwitch, { + // Deliberately the protocol chain, not `stellar.network`. Switch scopes + // are addressed with the chain an intent names ("stellar"); the network + // ("testnet"/"mainnet") selects a Soroban endpoint and would never match + // a `chain=stellar` pause. + chain: STELLAR_CHAIN, + token: null, + operation: "onchain", + }); + } catch (err) { + if (err instanceof KillSwitchActiveException) { + this.logger.warn( + `On-chain write blocked by kill-switch: method=${method} ` + + `scope=${err.scope} reason=${err.reasonCode}`, + ); + } + throw err; + } + } + + /** + * Simulates a contract invocation **without ever submitting it** (issue #401). + * + * This is the only RPC call the shadow-mode divergence monitor is allowed to + * make. `SorobanRpc.Server.simulateTransaction` runs the contract in a + * sandboxed copy of ledger state and returns a result without a transaction + * ever entering the mempool, so: + * + * - No transaction is signed, so no channel account sequence is consumed. + * - No fee is charged. + * - Ledger state is untouched. + * + * It is therefore safe to call regardless of the value of `ONCHAIN_DRY_RUN`: + * the flag governs *broadcast*, and this method never broadcasts. Calling it + * from a request path is still forbidden by the monitor's own contract (see + * `ShadowService.observe`), but the primitive itself is unconditionally + * read-only. + * + * Every failure mode is folded into a {@link SimulateContractResult} rather + * than a thrown error, so a caller draining a queue never has to distinguish + * "the contract said no" from "the RPC was down" by catching. + */ + async simulateContract(params: SimulateContractParams): Promise { + const sourceAccount = params.sourceAccount?.trim(); + if (!sourceAccount) { + return { + outcome: "skipped", + detail: "no simulation source account configured (SHADOW_SOURCE_ACCOUNT)", + }; + } + if (!params.contractId?.trim()) { + return { + outcome: "skipped", + detail: "no settlement contract configured (SETTLEMENT_CONTRACT_ID)", + }; + } + + let transaction: Transaction; + try { + transaction = await this.buildSimulationTransaction(params, sourceAccount); + } catch (err) { + // Building failed (bad contract ID, unparseable args, unreachable RPC for + // the sequence number). Nothing was broadcast, so this is safe to report — + // and it is `unavailable`, not `error`: the contract was never asked. + return { + outcome: "unavailable", + detail: `could not build simulation transaction: ${(err as Error).message}`, + }; + } + + let response: SorobanRpc.Api.SimulateTransactionResponse; + try { + response = await this.sorobanService.simulateTransaction(transaction); + } catch (err) { + // Transport failure: the contract was never asked, so this is our outage + // and not a disagreement with the contract. + return { + outcome: "unavailable", + detail: `simulation request failed: ${(err as Error).message}`, + }; + } + + if (!response) { + return { outcome: "unavailable", detail: "empty simulation response" }; + } + + // The RPC's success response has no `error` member; its error response has + // one. Reading it structurally keeps the shared classifier independent of + // the SDK's union type, and means the revert-vs-hard-error rule that decides + // `rejected` vs `error` is the single copy covered by + // `shadow-divergence.spec.ts` rather than a second one here. + const errorText = + "error" in response && typeof (response as { error?: unknown }).error === "string" + ? (response as { error: string }).error + : undefined; + const classification = classifySimulationResponse({ error: errorText }); + + if (classification.outcome === "ok" && !classification.threw) { + return { outcome: "ok" }; + } + + return { + outcome: classification.outcome === "rejected" ? "rejected" : "error", + ...(classification.detail ? { detail: classification.detail } : {}), + }; + } + + /** + * Assemble an unsigned, submit-shaped envelope for a contract invocation. + * + * The sequence number comes from the source account when it exists on chain. + * A simulation does not need a *correct* sequence — nothing is signed, so + * nothing is sequenced — but it does need the envelope to decode, and a + * contract that checks its own caller's sequence would answer a fabricated + * number differently than it answers the real one, manufacturing divergences + * out of nothing. + * + * A key that has never been on chain has no sequence, which is a legitimate + * configuration (a throwaway key is enough to build an envelope), so the + * latest ledger sequence is used as the fallback. + */ + private async buildSimulationTransaction( + params: SimulateContractParams, + sourceAccount: string, + ): Promise { + const baseFee = await this.estimateBaseFee(); + const sequence = await this.resolveSimulationSequence(sourceAccount); + + // `TransactionBuilder` emits `source.sequenceNumber() + 1` as the envelope's + // seqNum, so the account handed to it must sit one *below* the sequence the + // envelope should carry; passing `sequence` straight through would shift + // every envelope (42 -> 43, 501 -> 502, 0 -> 1). + const sourceSequence = (BigInt(sequence) - 1n).toString(); + + // Pin both ends of the window: `simulationTimeoutSeconds` sizes the + // *width* (worst-case queue drain), not "seconds from now", so the + // envelope does not silently stay valid for `now + window` seconds. + const now = Math.floor(Date.now() / 1000); + + return new TransactionBuilder(new Account(sourceAccount, sourceSequence), { + fee: baseFee, + networkPassphrase: this.networkPassphrase, + }) + // Same envelope shape as `invokeContract` builds for the live path — + // the monitor is only useful if it simulates the call the chain would + // actually receive. + .addOperation(new Contract(params.contractId).call(params.method, ...params.args)) + .setTimebounds(now, now + this.simulationTimeoutSeconds) + .addOperation( + // The SDK expects `func` to be a fully-formed xdr.HostFunction that + // already carries its InvokeContractArgs; a bare enum value (and the + // SDK-11 style `args` array) produces an envelope that cannot be XDR + // encoded. The token argument mirrors the settlement contract's + // `native` (XLM) entry point — irrelevant to a simulation, but the + // ScVal must be well-formed for the envelope to decode. + Operation.invokeHostFunction({ + func: xdr.HostFunction.hostFunctionTypeInvokeContract( + new xdr.InvokeContractArgs({ + contractAddress: contract.toScAddress(), + // InvokeContractArgs takes the method name as a plain string and + // encodes it as a symbol itself, so no nativeToScVal here. + functionName: params.method, + args: params.args, + }), + ), + auth: [], + }), + ) + .setTimeout(this.simulationTimeoutSeconds) + .build(); + } + + /** + * Best available sequence number for a simulation envelope. + * + * Tries the account first (exact), then the latest ledger (plausible), and + * finally `"0"`. Each fallback is logged at warn/debug so an operator reading + * the logs can tell a legitimate throwaway key from an RPC that is not + * answering. + */ + private async resolveSimulationSequence(sourceAccount: string): Promise { + try { + const account = await this.sorobanService.getAccount(sourceAccount); + const sequence = account.sequenceNumber(); + if (sequence) return String(sequence); + } catch (err) { + this.logger.warn( + `Could not load source account ${sourceAccount} for shadow simulation: ${ + (err as Error).message + }`, + ); + } + + try { + const ledger = await this.sorobanService.getLatestLedger(); + const latest = (ledger as unknown as { sequence?: string | number }).sequence; + const parsed = typeof latest === "string" ? Number(latest) : latest; + if (typeof parsed === "number" && Number.isFinite(parsed)) { + return String(parsed + 1); + } + } catch (err) { + this.logger.warn( + `Could not read latest ledger for shadow simulation, falling back to sequence 0: ${ + (err as Error).message + }`, + ); + } + + return "0"; + } +} diff --git a/src/soroban/tx-confirmation.service.spec.ts b/src/soroban/tx-confirmation.service.spec.ts index e69de29b..f4814bfb 100644 --- a/src/soroban/tx-confirmation.service.spec.ts +++ b/src/soroban/tx-confirmation.service.spec.ts @@ -0,0 +1,203 @@ +import { SorobanRpc } from "@stellar/stellar-sdk"; +import { TxConfirmationService, TrackTxOptions } from "./tx-confirmation.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { MetricsService } from "../metrics/metrics.service"; +import { SorobanService } from "./soroban.service"; +import { SignerService } from "./signer.service"; +import { FeeEscalationPolicy } from "./fee-escalation-policy"; +import { TxConfirmed, TxFailed, TxExpired } from "./tx-events"; + +// EventEmitter2 is ESM-only; mock it for Jest's CommonJS environment +jest.mock("@nestjs/event-emitter", () => ({ + EventEmitter2: jest.fn().mockImplementation(() => ({ emit: jest.fn() })), +})); + +function makePrisma(overrides: Partial<{ + queryRaw: jest.Mock; + update: jest.Mock; + upsert: jest.Mock; +}> = {}): PrismaService { + return { + pendingTransaction: { + upsert: overrides.upsert ?? jest.fn().mockResolvedValue({}), + update: overrides.update ?? jest.fn().mockResolvedValue({}), + }, + $queryRaw: overrides.queryRaw ?? jest.fn().mockResolvedValue([]), + } as unknown as PrismaService; +} + +function makeSoroban( + getTransactionResult: SorobanRpc.Api.GetTransactionResponse, +): SorobanService { + return { + getTransaction: jest.fn().mockResolvedValue(getTransactionResult), + submitTransaction: jest.fn().mockResolvedValue({ status: "PENDING" }), + } as unknown as SorobanService; +} + +function makeSigner(configured = false): SignerService { + return { + isConfigured: jest.fn().mockReturnValue(configured), + getNetworkPassphrase: jest.fn().mockReturnValue("Test SDF Network ; September 2015"), + getSecretKey: jest.fn().mockReturnValue(""), + } as unknown as SignerService; +} + +function makeMetrics(): MetricsService { + return { + txConfirmationOutcomes: { inc: jest.fn() }, + txConfirmationLatency: { observe: jest.fn() }, + } as unknown as MetricsService; +} + +function makeFeePolicy(shouldEscalate = false): FeeEscalationPolicy { + return { + shouldEscalate: jest.fn().mockReturnValue(shouldEscalate), + buildFeeBump: jest.fn().mockResolvedValue(null), + } as unknown as FeeEscalationPolicy; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function makeEvents(): any { + return { emit: jest.fn() }; +} + +const BASE_ROW = { + id: 1n, + tx_hash: "abc123", + tx_xdr: "AAAA", + intent_id: "intent-1", + channel_key: null, + status: "pending", + max_track_until: Math.floor(Date.now() / 1000) + 300, + attempts: 0, + next_poll_at: Math.floor(Date.now() / 1000) - 1, + last_fee_stroops: null, + fee_bump_count: 0, + created_at: new Date(), +}; + +describe("TxConfirmationService", () => { + describe("track()", () => { + it("upserts a pending transaction record", async () => { + const upsert = jest.fn().mockResolvedValue({}); + const prisma = makePrisma({ upsert }); + const svc = new TxConfirmationService( + prisma, + makeSoroban({ status: SorobanRpc.Api.GetTransactionStatus.NOT_FOUND } as SorobanRpc.Api.GetTransactionResponse), + makeSigner(), + makeFeePolicy(), + makeMetrics(), + makeEvents(), + ); + + const opts: TrackTxOptions = { + txHash: "abc123", + txXdr: "AAAA", + intentId: "intent-1", + maxTrackUntil: Math.floor(Date.now() / 1000) + 300, + }; + await svc.track(opts); + + expect(upsert).toHaveBeenCalledWith( + expect.objectContaining({ + where: { txHash: "abc123" }, + create: expect.objectContaining({ txHash: "abc123", status: "pending" }), + }), + ); + }); + }); + + describe("processRow() via pollBatch()", () => { + it("marks confirmed and emits TxConfirmed on SUCCESS", async () => { + const update = jest.fn().mockResolvedValue({}); + const prisma = makePrisma({ queryRaw: jest.fn().mockResolvedValue([BASE_ROW]), update }); + const soroban = makeSoroban({ + status: SorobanRpc.Api.GetTransactionStatus.SUCCESS, + ledger: 42, + } as SorobanRpc.Api.GetSuccessfulTransactionResponse); + const events = makeEvents(); + const metrics = makeMetrics(); + + const svc = new TxConfirmationService( + prisma, soroban, makeSigner(), makeFeePolicy(), metrics, events, + ); + + // Call the private pollBatch via onModuleInit's interval—instead directly + // trigger it by casting to any + await (svc as unknown as { pollBatch: () => Promise }).pollBatch(); + + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ data: { status: "confirmed" } }), + ); + expect(events.emit).toHaveBeenCalledWith(TxConfirmed.EVENT, expect.any(TxConfirmed)); + expect(metrics.txConfirmationOutcomes.inc).toHaveBeenCalledWith({ status: "confirmed" }); + }); + + it("marks failed and emits TxFailed on FAILED (no fee bump)", async () => { + const update = jest.fn().mockResolvedValue({}); + const prisma = makePrisma({ queryRaw: jest.fn().mockResolvedValue([BASE_ROW]), update }); + const soroban = makeSoroban({ + status: SorobanRpc.Api.GetTransactionStatus.FAILED, + } as SorobanRpc.Api.GetFailedTransactionResponse); + const events = makeEvents(); + const metrics = makeMetrics(); + + const svc = new TxConfirmationService( + prisma, soroban, makeSigner(false), makeFeePolicy(false), metrics, events, + ); + await (svc as unknown as { pollBatch: () => Promise }).pollBatch(); + + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ data: { status: "failed" } }), + ); + expect(events.emit).toHaveBeenCalledWith(TxFailed.EVENT, expect.any(TxFailed)); + }); + + it("schedules retry with backoff on NOT_FOUND", async () => { + const update = jest.fn().mockResolvedValue({}); + const prisma = makePrisma({ queryRaw: jest.fn().mockResolvedValue([BASE_ROW]), update }); + const soroban = makeSoroban({ + status: SorobanRpc.Api.GetTransactionStatus.NOT_FOUND, + } as SorobanRpc.Api.GetTransactionResponse); + + const svc = new TxConfirmationService( + prisma, soroban, makeSigner(), makeFeePolicy(false), makeMetrics(), makeEvents(), + ); + await (svc as unknown as { pollBatch: () => Promise }).pollBatch(); + + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ attempts: 1 }), + }), + ); + }); + + it("marks expired and emits TxExpired when past maxTrackUntil", async () => { + const expiredRow = { + ...BASE_ROW, + max_track_until: Math.floor(Date.now() / 1000) - 10, // already expired + }; + const update = jest.fn().mockResolvedValue({}); + const prisma = makePrisma({ queryRaw: jest.fn().mockResolvedValue([expiredRow]), update }); + const events = makeEvents(); + const metrics = makeMetrics(); + + const svc = new TxConfirmationService( + prisma, + makeSoroban({ status: SorobanRpc.Api.GetTransactionStatus.NOT_FOUND } as SorobanRpc.Api.GetTransactionResponse), + makeSigner(), + makeFeePolicy(), + metrics, + events, + ); + await (svc as unknown as { pollBatch: () => Promise }).pollBatch(); + + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ data: { status: "expired" } }), + ); + expect(events.emit).toHaveBeenCalledWith(TxExpired.EVENT, expect.any(TxExpired)); + expect(metrics.txConfirmationOutcomes.inc).toHaveBeenCalledWith({ status: "expired" }); + }); + }); +}); diff --git a/src/soroban/tx-confirmation.service.ts b/src/soroban/tx-confirmation.service.ts index e69de29b..0622f9aa 100644 --- a/src/soroban/tx-confirmation.service.ts +++ b/src/soroban/tx-confirmation.service.ts @@ -0,0 +1,116 @@ +/** + * TxConfirmationService (issue #394) + * ──────────────────────────────────── + * Polls the Soroban RPC until a submitted transaction reaches a terminal + * status (SUCCESS or FAILED) or the configured timeout elapses. + * + * Used by StellarTxService after every live-path submitTransaction call so + * callers receive a definitive result rather than an optimistic "sent". + * + * Metrics: records fill-to-confirmation latency via MetricsService + * (vortex_tx_confirmation_duration_seconds, SLO SLI from issue #480). + */ + +import { Injectable, Logger, Optional } from "@nestjs/common"; +import { SorobanRpc } from "@stellar/stellar-sdk"; +import { SorobanService } from "./soroban.service"; +import { MetricsService } from "../metrics/metrics.service"; + +const DEFAULT_POLL_INTERVAL_MS = 3_000; +const DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes + +export interface ConfirmationResult { + hash: string; + status: "SUCCESS" | "FAILED" | "TIMEOUT"; + /** Full RPC response when status is SUCCESS or FAILED. */ + response?: SorobanRpc.Api.GetTransactionResponse; + /** Error message when status is FAILED or TIMEOUT. */ + error?: string; + /** Wall-clock milliseconds from first poll until terminal status. */ + durationMs: number; +} + +@Injectable() +export class TxConfirmationService { + private readonly logger = new Logger(TxConfirmationService.name); + + constructor( + private readonly sorobanService: SorobanService, + @Optional() private readonly metricsService?: MetricsService, + ) {} + + /** + * Poll until the transaction identified by `hash` reaches a terminal state. + * + * @param hash Transaction hash returned by `sendTransaction`. + * @param submittedAt Unix-ms timestamp when the transaction was submitted + * (used to compute confirmation latency for the SLO SLI). + * @param pollIntervalMs How often to poll (default 3 s). + * @param timeoutMs Give-up threshold (default 2 min). + */ + async waitForConfirmation( + hash: string, + submittedAt: number, + pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, + timeoutMs = DEFAULT_TIMEOUT_MS, + ): Promise { + const deadline = Date.now() + timeoutMs; + + while (Date.now() < deadline) { + let response: SorobanRpc.Api.GetTransactionResponse; + try { + response = await this.sorobanService.getTransaction(hash); + } catch (err) { + this.logger.warn( + `[tx-confirmation] getTransaction(${hash}) threw: ${(err as Error).message}; retrying`, + ); + await sleep(pollIntervalMs); + continue; + } + + if (response.status === SorobanRpc.Api.GetTransactionStatus.NOT_FOUND) { + await sleep(pollIntervalMs); + continue; + } + + const durationMs = Date.now() - submittedAt; + + if (response.status === SorobanRpc.Api.GetTransactionStatus.SUCCESS) { + this.logger.log( + `[tx-confirmation] SUCCESS hash=${hash} durationMs=${durationMs}`, + ); + try { + this.metricsService?.observeTxConfirmation(durationMs / 1000); + } catch { + /* metrics must never throw */ + } + return { hash, status: "SUCCESS", response, durationMs }; + } + + // FAILED: `response` is narrowed to the failed variant, whose `resultXdr` + // is a decoded xdr.TransactionResult (not a string). + const errorDetail = response.resultXdr.result().switch().name; + // FAILED + const errorDetail = (response as unknown as { resultXdr?: string }).resultXdr ?? "unknown"; + this.logger.warn( + `[tx-confirmation] FAILED hash=${hash} durationMs=${durationMs} detail=${errorDetail}`, + ); + return { hash, status: "FAILED", response, error: errorDetail, durationMs }; + } + + const durationMs = Date.now() - submittedAt; + this.logger.warn( + `[tx-confirmation] TIMEOUT hash=${hash} after ${durationMs}ms`, + ); + return { + hash, + status: "TIMEOUT", + error: `Transaction not confirmed within ${timeoutMs}ms`, + durationMs, + }; + } +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/src/stats/stats.service.ts b/src/stats/stats.service.ts index e69de29b..bfc93b2c 100644 --- a/src/stats/stats.service.ts +++ b/src/stats/stats.service.ts @@ -0,0 +1,215 @@ +import { Injectable, Optional } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { createHmac } from "crypto"; +import { AppConfig } from "../config/configuration"; +import { isCanaryIntent } from "../common/canary"; +import { IntentsService } from "../intents/intents.service"; +import { SUPPORTED_CHAINS } from "../intents/intents.types"; +import { SolversService } from "../solvers/solvers.service"; +import { IntentsGateway } from "../intents/intents.gateway"; +import { AbuseScoreService } from "../abuse/abuse-score.service"; +import { CHALLENGE_THRESHOLD } from "../abuse/abuse.types"; + +@Injectable() +export class StatsService { + constructor( + private readonly intentsService: IntentsService, + private readonly solversService: SolversService, + private readonly intentsGateway: IntentsGateway, + @Optional() private readonly abuseScorer?: AbuseScoreService, + @Optional() config?: ConfigService, + ) { + this.canary = new Set(config?.get("canaryAddresses", { infer: true }) ?? []); + } + + /** Canary addresses (issue #496) — their intents and solvers never count toward public stats. */ + private readonly canary: ReadonlySet; + + private async publicIntents() { + return (await this.intentsService.getAll()).filter((i) => !isCanaryIntent(i, this.canary)); + } + + /** + * Returns intents for stat computation, optionally excluding flagged (high-abuse-score) actors. + * + * When `includeFlagged` is false (the default), intents from users whose cached + * abuse score is above the challenge threshold are excluded so spam does not + * inflate governance and incentive metrics. + * + * Operators can toggle `includeFlagged=true` on any stats endpoint for a + * full-transparency view that shows the raw counts before filtering. + */ + private async publicIntentsFiltered(includeFlagged = false) { + const intents = await this.publicIntents(); + if (includeFlagged || !this.abuseScorer) return intents; + + // Fetch cached scores for all unique users in parallel (best-effort: missing + // score = unknown = keep). + const uniqueUsers = [...new Set(intents.map((i) => i.user))]; + const scores = await Promise.all( + uniqueUsers.map(async (u) => [u, await this.abuseScorer!.getCachedScore(u)] as const), + ); + const flaggedUsers = new Set( + scores.filter(([, score]) => score !== null && score >= CHALLENGE_THRESHOLD).map(([u]) => u), + ); + + return intents.filter((i) => !flaggedUsers.has(i.user)); + } + + private canonicalJson(value: unknown): string { + const seen = new WeakSet(); + + const normalize = (input: unknown): unknown => { + if (Array.isArray(input)) { + return input.map((item) => normalize(item)); + } + if (input && typeof input === "object") { + if (seen.has(input)) { + return "[Circular]"; + } + seen.add(input); + return Object.keys(input as Record) + .sort() + .reduce>((acc, key) => { + acc[key] = normalize((input as Record)[key]); + return acc; + }, {}); + } + return input; + }; + + return JSON.stringify(normalize(value)); + } + + private getPublicDatasetUrl() { + return process.env.PUBLIC_STATS_DATASET_URL ?? "https://example.invalid/public-stats/latest.json"; + } + + private getPublicSigningKey() { + return process.env.PUBLIC_STATS_SIGNING_KEY ?? "public-stats-dev-key"; + } + + private getProvenance(payload: Record) { + const safePayload = { ...payload }; + delete safePayload.provenance; + const generatedAt = new Date().toISOString(); + const signature = createHmac("sha256", this.getPublicSigningKey()) + .update(this.canonicalJson({ ...safePayload, generatedAt })) + .digest("hex"); + + return { + watermarkLedger: 0, + generatedAt, + queryVersion: "public-stats/v1", + datasetUrl: this.getPublicDatasetUrl(), + signature, + }; + } + + async getProtocolStats(includeFlagged = false) { + const intents = await this.publicIntentsFiltered(includeFlagged); + const solvers = (await this.solversService.getAll()).filter((s) => !this.canary.has(s.address)); + + const open = intents.filter((i) => i.state === "open").length; + const filled = intents.filter((i) => i.state === "filled"); + const totalVolume = filled.reduce((sum, i) => sum + BigInt(i.fillAmount ?? "0"), 0n); + + const fillTimes = filled + .filter((i) => i.filledAt != null) + .map((i) => i.filledAt! - i.createdAt); + const avgFillTime = fillTimes.length + ? fillTimes.reduce((a, b) => a + b, 0) / fillTimes.length + : 0; + + return { + totalIntents: intents.length, + openIntents: open, + totalVolume: totalVolume.toString(), + uniqueUsers: new Set(intents.map((i) => i.user)).size, + activeSolvers: solvers.filter((s) => s.isActive).length, + avgFillTime: Math.round(avgFillTime), + fillRate: intents.length ? filled.length / intents.length : 0, + flaggedActivityExcluded: !includeFlagged, + }; + } + + async getPublicStats(includeFlagged = false) { + const stats = await this.getProtocolStats(includeFlagged); + return { + ...stats, + provenance: this.getProvenance(stats), + }; + } + + getPublicStatsHistory() { + return [] as Array>; + } + + async getTreasuryStats(includeFlagged = false) { + const intents = await this.publicIntentsFiltered(includeFlagged); + const now = Math.floor(Date.now() / 1000); + const last24hCutoff = now - 86_400; + + const allTime = intents + .filter((intent) => typeof intent.feeAmount === "string" && intent.feeAmount.length > 0) + .reduce((sum, intent) => sum + BigInt(intent.feeAmount ?? "0"), 0n); + + const last24h = intents + .filter( + (intent) => + typeof intent.feeAmount === "string" && + intent.feeAmount.length > 0 && + typeof intent.filledAt === "number" && + intent.filledAt >= last24hCutoff, + ) + .reduce((sum, intent) => sum + BigInt(intent.feeAmount ?? "0"), 0n); + + const byChain = new Map(); + + for (const intent of intents) { + if (typeof intent.feeAmount !== "string" || intent.feeAmount.length === 0) continue; + const fee = BigInt(intent.feeAmount ?? "0"); + const entry = byChain.get(intent.srcChain) ?? { + totalFees: 0n, + last24hFees: 0n, + filledCount: 0, + }; + + entry.totalFees += fee; + entry.filledCount += 1; + if (typeof intent.filledAt === "number" && intent.filledAt >= last24hCutoff) { + entry.last24hFees += fee; + } + byChain.set(intent.srcChain, entry); + } + + return { + allTime: { + totalFees: allTime.toString(), + filledIntents: intents.filter((intent) => typeof intent.feeAmount === "string" && intent.feeAmount.length > 0).length, + }, + last24h: { + totalFees: last24h.toString(), + filledIntents: intents.filter( + (intent) => + typeof intent.feeAmount === "string" && + intent.feeAmount.length > 0 && + typeof intent.filledAt === "number" && + intent.filledAt >= last24hCutoff, + ).length, + }, + byChain: Array.from(byChain.entries()).map(([srcChain, stats]) => ({ + srcChain, + totalFees: stats.totalFees.toString(), + last24hFees: stats.last24hFees.toString(), + filledIntents: stats.filledCount, + })), + }; + } + + getWsStats() { + return { + subscriberCount: this.intentsGateway.getSubscriberCount(), + }; + } +} diff --git a/src/tokens/in-memory-tokens.repository.ts b/src/tokens/in-memory-tokens.repository.ts index e69de29b..ed263c82 100644 --- a/src/tokens/in-memory-tokens.repository.ts +++ b/src/tokens/in-memory-tokens.repository.ts @@ -0,0 +1,49 @@ +import { SupportedChain } from "../intents/intents.types"; +import { STELLAR_TOKENS, SUPPORTED_TOKENS } from "./tokens.data"; +import { ITokensRepository, TokenRecord } from "./tokens.repository"; + +export class InMemoryTokensRepository implements ITokensRepository { + private readonly records: TokenRecord[] = [ + ...Object.entries(SUPPORTED_TOKENS).flatMap(([chain, tokens]) => + tokens.map((token) => ({ + address: token.address, + symbol: token.symbol, + name: token.name, + decimals: token.decimals, + chain: chain as SupportedChain, + priceUsd: token.priceUSD, + isStellar: false, + })), + ), + ...STELLAR_TOKENS.map((token) => ({ + address: token.contract, + symbol: token.symbol, + name: token.name, + decimals: token.decimals, + chain: "stellar" as const, + priceUsd: token.priceUSD, + isStellar: true, + })), + ]; + + findAll(): TokenRecord[] { + return this.records.map((record) => ({ ...record })); + } + + findByChain(chain: SupportedChain | string): TokenRecord[] { + const normalized = String(chain).toLowerCase(); + return this.records + .filter((record) => record.chain === normalized || record.chain === chain) + .map((record) => ({ ...record })); + } + + findByAddressAndChain(address: string, chain: SupportedChain | string): TokenRecord | undefined { + const normalizedAddress = address.trim().toLowerCase(); + const chainName = String(chain).toLowerCase(); + const match = this.records.find( + (record) => + record.address.toLowerCase() === normalizedAddress && record.chain === chainName, + ); + return match ? { ...match } : undefined; + } +} diff --git a/src/tokens/tokens.service.spec.ts b/src/tokens/tokens.service.spec.ts index e69de29b..c79e92ee 100644 --- a/src/tokens/tokens.service.spec.ts +++ b/src/tokens/tokens.service.spec.ts @@ -0,0 +1,181 @@ +import { BadRequestException } from "@nestjs/common"; +import { InMemoryTokensRepository } from "./in-memory-tokens.repository"; +import { TokensService } from "./tokens.service"; +import { SUPPORTED_TOKENS, STELLAR_TOKENS } from "./tokens.data"; + +/** + * TokensService is backed by ITokensRepository so the registry can move to + * Postgres without touching callers. The in-memory adapter is injected here + * and the async read methods are awaited, matching the production shape. + */ +describe("TokensService", () => { + let service: TokensService; + + beforeEach(() => { + service = new TokensService(new InMemoryTokensRepository()); + }); + + it("getByChain with no chain returns the full registry plus Stellar tokens", async () => { + const result = await service.getByChain(); + // Both token maps present + expect(result).toHaveProperty("tokens"); + expect(result).toHaveProperty("stellarTokens"); + expect(result.tokens).toHaveProperty("ethereum"); + }); + + it("getByChain('stellar') returns only Stellar tokens", async () => { + const result = await service.getByChain("stellar"); + expect(result.chain).toBe("stellar"); + expect(Array.isArray(result.tokens)).toBe(true); + }); + + it("getByChain with a known chain returns that chain's tokens", async () => { + const result = await service.getByChain("polygon"); + expect(result.chain).toBe("polygon"); + expect(Array.isArray(result.tokens)).toBe(true); + }); + + it("getByChain with an unknown chain falls back to the full registry", async () => { + const result = await service.getByChain("not-a-real-chain"); + expect(result).toHaveProperty("tokens"); + expect(result.tokens).toHaveProperty("ethereum"); + }); + + it("getStellarTokens returns the Stellar token list", async () => { + const result = await service.getStellarTokens(); + expect(Array.isArray(result.tokens)).toBe(true); + expect(result.tokens.length).toBeGreaterThan(0); + }); + + // ── resolveSrcToken ────────────────────────────────────────────────────── + + describe("resolveSrcToken", () => { + it("resolves a known Ethereum token by address", async () => { + const usdcAddr = SUPPORTED_TOKENS["ethereum"][0].address; + const result = await service.resolveSrcToken("ethereum", usdcAddr); + expect(result).toBeDefined(); + expect(result!.kind).toBe("src"); + expect(result!.symbol).toBe("USDC"); + expect(result!.chain).toBe("ethereum"); + expect(typeof result!.priceUSD).toBe("number"); + }); + + it("resolves a known Base token", async () => { + const addr = SUPPORTED_TOKENS["base"][0].address; + const result = await service.resolveSrcToken("base", addr); + expect(result).toBeDefined(); + expect(result!.chain).toBe("base"); + }); + + it("resolves a known Polygon token", async () => { + const addr = SUPPORTED_TOKENS["polygon"][0].address; + const result = await service.resolveSrcToken("polygon", addr); + expect(result).toBeDefined(); + expect(result!.chain).toBe("polygon"); + }); + + it("resolves a known Arbitrum token", async () => { + const addr = SUPPORTED_TOKENS["arbitrum"][0].address; + const result = await service.resolveSrcToken("arbitrum", addr); + expect(result).toBeDefined(); + expect(result!.chain).toBe("arbitrum"); + }); + + it("resolves a Stellar source token by contract ID", async () => { + const contract = STELLAR_TOKENS[0].contract; + const result = await service.resolveSrcToken("stellar", contract); + expect(result).toBeDefined(); + expect(result!.kind).toBe("src"); + expect(result!.chain).toBe("stellar"); + expect(result!.address).toBe(contract); + }); + + it("returns undefined for an unknown ethereum address", async () => { + expect(await service.resolveSrcToken("ethereum", "0xdeadbeef")).toBeUndefined(); + }); + + it("returns undefined for an unknown stellar contract", async () => { + expect(await service.resolveSrcToken("stellar", "CUNKNOWN")).toBeUndefined(); + }); + + it("returns undefined for an unknown chain", async () => { + // "optimism" is in the SUPPORTED_TOKENS registry but let's verify a truly unknown chain + expect(await service.resolveSrcToken("avalanche" as never, "0xunknown")).toBeUndefined(); + }); + }); + + // ── resolveDstToken ────────────────────────────────────────────────────── + + describe("resolveDstToken", () => { + it("resolves a known Stellar USDC contract", async () => { + const contract = STELLAR_TOKENS[0].contract; // USDC + const result = await service.resolveDstToken(contract); + expect(result).toBeDefined(); + expect(result!.kind).toBe("dst"); + expect(result!.symbol).toBe("USDC"); + expect(result!.contract).toBe(contract); + expect(typeof result!.priceUSD).toBe("number"); + }); + + it("resolves XLM contract", async () => { + const xlm = STELLAR_TOKENS.find((t) => t.symbol === "XLM")!; + const result = await service.resolveDstToken(xlm.contract); + expect(result).toBeDefined(); + expect(result!.symbol).toBe("XLM"); + }); + + it("returns undefined for an unknown contract", async () => { + expect(await service.resolveDstToken("CNOTEXIST")).toBeUndefined(); + }); + + it("returns undefined for an empty string", async () => { + expect(await service.resolveDstToken("")).toBeUndefined(); + }); + }); + + // ── #276: OrThrow variants reject unrecognised tokens ───────────────────── + + describe("resolveSrcTokenOrThrow", () => { + it("returns the resolved token for a known chain + address", async () => { + const usdcAddr = SUPPORTED_TOKENS["ethereum"][0].address; + const result = await service.resolveSrcTokenOrThrow("ethereum", usdcAddr); + expect(result.symbol).toBe("USDC"); + expect(result.priceUSD).toBe(1.0); + }); + + it("throws BadRequestException for an unknown address on a known chain", async () => { + await expect( + service.resolveSrcTokenOrThrow( + "ethereum", + "0x1111111111111111111111111111111111111111", + ), + ).rejects.toBeInstanceOf(BadRequestException); + }); + + it("throws BadRequestException for an unknown Stellar source contract", async () => { + await expect(service.resolveSrcTokenOrThrow("stellar", "CUNKNOWN")).rejects.toBeInstanceOf( + BadRequestException, + ); + }); + }); + + describe("resolveDstTokenOrThrow", () => { + it("returns the resolved token for a known Stellar contract", async () => { + const contract = STELLAR_TOKENS[0].contract; + const result = await service.resolveDstTokenOrThrow(contract); + expect(result.contract).toBe(contract); + }); + + it("throws BadRequestException for an unknown contract", async () => { + await expect(service.resolveDstTokenOrThrow("CNOTEXIST")).rejects.toBeInstanceOf( + BadRequestException, + ); + }); + + it("throws BadRequestException for an empty contract", async () => { + await expect(service.resolveDstTokenOrThrow("")).rejects.toBeInstanceOf( + BadRequestException, + ); + }); + }); +}); diff --git a/src/tokens/tokens.service.ts b/src/tokens/tokens.service.ts index e69de29b..11ced880 100644 --- a/src/tokens/tokens.service.ts +++ b/src/tokens/tokens.service.ts @@ -0,0 +1,222 @@ +import { BadRequestException, Inject, Injectable } from "@nestjs/common"; +import { SUPPORTED_TOKENS, StellarToken } from "./tokens.data"; +import { SUPPORTED_TOKENS, STELLAR_TOKENS, StellarToken } from "./tokens.data"; +import { SupportedChain } from "../intents/intents.types"; +import { ITokensRepository, TOKENS_REPOSITORY, TokenRecord } from "./tokens.repository"; + +/** + * A resolved source-chain (EVM or Stellar source) token — always has a + * canonical `address` field used by TokensService.resolveToken(). + */ +export interface ResolvedSrcToken { + kind: "src"; + address: string; + symbol: string; + name: string; + decimals: number; + chain: SupportedChain; + priceUSD: number; +} + +/** + * A resolved Stellar destination token. + */ +export interface ResolvedDstToken { + kind: "dst"; + contract: string; + symbol: string; + name: string; + decimals: number; + priceUSD: number; +} + +export type ResolvedToken = ResolvedSrcToken | ResolvedDstToken; + +export interface ApiToken { + address: string; + contract: string; + symbol: string; + name: string; + decimals: number; + priceUSD: number; +} + +export interface TokensByChainResponse { + tokens: ApiToken[] | Record; + chain?: string; + stellarTokens?: ApiToken[]; +} + +@Injectable() +export class TokensService { + constructor( + @Inject(TOKENS_REPOSITORY) + private readonly repo: ITokensRepository, + ) {} + + /** + * Look up a source token by chain + address/contract. + * + * For Stellar source tokens the `address` parameter is the contract ID. + * For EVM chains it is the checksummed hex address. + * + * Returns `undefined` when no match is found — callers decide how to handle + * the "unknown token" case (e.g. fall back to a default priceUSD). + * + * @param chain The source chain (stellar | ethereum | base | …) + * @param address Token contract/address string + */ + async resolveSrcToken(chain: SupportedChain, address: string): Promise { + const token = await this.repo.findByAddressAndChain(address, chain); + if (!token) return undefined; + return { + kind: "src", + address: token.address, + symbol: token.symbol, + name: token.name, + decimals: token.decimals, + chain, + priceUSD: token.priceUsd ?? 0, + }; + } + + /** + * Look up a Stellar destination token by contract ID. + * + * Returns `undefined` when no match is found. + */ + async resolveDstToken(contract: string): Promise { + const token = await this.repo.findByAddressAndChain(contract, "stellar"); + if (!token) return undefined; + return { + kind: "dst", + contract: token.address, + symbol: token.symbol, + name: token.name, + decimals: token.decimals, + priceUSD: token.priceUsd ?? 0, + }; + } + + /** + * Like {@link resolveSrcToken} but throws a `BadRequestException` instead of + * returning `undefined` when the chain + address does not resolve to a token + * in the configured registry (issue #276). + * + * Use this on the write path (intent creation) where an unrecognised token + * must be rejected outright rather than silently stored with no priceUSD. + */ + async resolveSrcTokenOrThrow( + chain: SupportedChain, + address: string, + ): Promise { + const token = await this.resolveSrcToken(chain, address); + if (!token) { + throw new BadRequestException( + `Unknown source token '${address}' for chain '${chain}' in the configured token registry`, + ); + } + return token; + } + + /** + * Like {@link resolveDstToken} but throws a `BadRequestException` instead of + * returning `undefined` when the contract does not resolve to a known Stellar + * token (issue #276). + */ + async resolveDstTokenOrThrow(contract: string): Promise { + const token = await this.resolveDstToken(contract); + if (!token) { + throw new BadRequestException( + "Unknown destination token contract for the configured token registry", + ); + } + return token; + } + + /** + * Normalise a stored {@link TokenRecord} into the public token shape. + * + * Both `address` and `contract` are emitted with the same value so clients + * can read either field regardless of whether the token is EVM- or + * Stellar-native — the registry stores every token under `address`, but the + * Stellar side of the API has always used `contract`. + */ + private toApiToken(record: TokenRecord): ApiToken { + return { + address: record.address, + contract: record.address, + symbol: record.symbol, + name: record.name, + decimals: record.decimals, + priceUSD: record.priceUsd ?? 0, + }; + } + + /** + * Return the supported token registry, optionally narrowed to one chain. + * + * - `chain="stellar"` → `{ tokens: StellarToken[], chain: "stellar" }` + * - `chain=` → `{ tokens: Token[], chain }` + * - omitted / unknown → `{ tokens: Record, stellarTokens: Token[] }` + * + * An unrecognised chain deliberately falls back to the full registry rather + * than erroring: this endpoint feeds discovery UIs, and a client with a + * stale chain list should see everything, not a 4xx. + */ + async getByChain(chain?: string): Promise { + const requested = chain?.toLowerCase(); + + if (requested === "stellar") { + const records = await this.repo.findByChain("stellar"); + return { + tokens: records.map((record) => this.toApiToken(record)), + chain: "stellar", + }; + } + + if (requested && requested in SUPPORTED_TOKENS) { + const records = await this.repo.findByChain(requested); + return { + tokens: records + .filter((record) => record.chain === requested) + .map((record) => this.toApiToken(record)), + chain: requested, + }; + } + + const all = await this.repo.findAll(); + + // Bucket by chain, pre-seeding a key for every chain the static registry + // declares so a chain with no rows still appears as an empty array rather + // than vanishing from the response shape. + const byChain: Record = {}; + for (const key of Object.keys(SUPPORTED_TOKENS)) { + byChain[key] = []; + } + for (const record of all) { + if (!byChain[record.chain]) byChain[record.chain] = []; + byChain[record.chain].push(this.toApiToken(record)); + } + + return { + tokens: byChain, + stellarTokens: all + .filter((record) => record.chain === "stellar") + .map((record) => this.toApiToken(record)), + }; + } + + async getStellarTokens(): Promise<{ tokens: StellarToken[] }> { + const records = await this.repo.findByChain("stellar"); + return { + tokens: records.map((record) => ({ + contract: record.address, + symbol: record.symbol, + name: record.name, + decimals: record.decimals, + priceUSD: record.priceUsd ?? 0, + })), + }; + } +} diff --git a/src/treasury/treasury.service.spec.ts b/src/treasury/treasury.service.spec.ts index e69de29b..c1707433 100644 --- a/src/treasury/treasury.service.spec.ts +++ b/src/treasury/treasury.service.spec.ts @@ -0,0 +1,520 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { ConfigService } from "@nestjs/config"; +import { TreasuryService } from "./treasury.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { SorobanService } from "../soroban/soroban.service"; + +/** + * The Prisma delegates this suite stubs. `jest.Mocked` is shallow, so the + * model delegates (objects, not methods) would keep their real Prisma types; + * this local shape keeps every stub a `jest.Mock` with `mockResolvedValue`. + */ +type MockPrisma = { + feeLedger: { create: jest.Mock; findMany: jest.Mock }; + slashLedger: { create: jest.Mock; findMany: jest.Mock }; + refundLedger: { create: jest.Mock; findMany: jest.Mock }; + treasurySnapshot: { upsert: jest.Mock; findMany: jest.Mock; findUnique: jest.Mock }; +}; + +describe("TreasuryService", () => { + let service: TreasuryService; + let prisma: MockPrisma; + // The mock Prisma only carries the ledger delegates the service touches; + // type as `any` so the per-method `mockResolvedValue` calls type-check + // (jest.Mocked does not deep-transform nested Prisma delegates). + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let prisma: any; + let soroban: jest.Mocked; + let configService: jest.Mocked; + + const mockTreasuryAddress = "GTREASURY123456789"; + + beforeEach(async () => { + const mockPrisma = { + feeLedger: { + create: jest.fn(), + findMany: jest.fn(), + }, + slashLedger: { + create: jest.fn(), + findMany: jest.fn(), + }, + refundLedger: { + create: jest.fn(), + findMany: jest.fn(), + }, + treasurySnapshot: { + upsert: jest.fn(), + findMany: jest.fn(), + findUnique: jest.fn(), + }, + }; + + const mockConfigService = { + get: jest.fn((key: string) => { + if (key === "treasury.address") return mockTreasuryAddress; + if (key === "stellar.horizonUrl") return "https://horizon-testnet.stellar.org"; + if (key === "stellar.sorobanRpcUrl") return "https://soroban-testnet.stellar.org"; + return null; + }), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + TreasuryService, + { provide: PrismaService, useValue: mockPrisma }, + { provide: SorobanService, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService }, + ], + }).compile(); + + service = module.get(TreasuryService); + prisma = mockPrisma; + // The mock Prisma only carries the ledger delegates the service touches; + // cast to `any` so the per-method `mockResolvedValue` calls type-check + // (jest.Mocked does not deep-transform nested Prisma delegates). + // eslint-disable-next-line @typescript-eslint/no-explicit-any + prisma = module.get(PrismaService) as any; + soroban = module.get(SorobanService) as jest.Mocked; + configService = module.get(ConfigService) as jest.Mocked; + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); + + describe("recordFee", () => { + it("should record a fee in the ledger", async () => { + const feeEntry = { + intentId: "intent-123", + asset: "native", + amount: "1000000", + accrualAt: new Date(), + txHash: "tx-hash", + }; + + prisma.feeLedger.create.mockResolvedValue({ + id: 1n, + ...feeEntry, + } as any); + + await service.recordFee(feeEntry); + + expect(prisma.feeLedger.create).toHaveBeenCalledWith({ + data: feeEntry, + }); + }); + }); + + describe("recordSlash", () => { + it("should record a slash in the ledger", async () => { + const slashEntry = { + solverAddress: "solver-123", + asset: "native", + amount: "5000000", + slashedAt: new Date(), + reason: "Missed deadline", + txHash: "tx-hash", + }; + + prisma.slashLedger.create.mockResolvedValue({ + id: 1n, + ...slashEntry, + } as any); + + await service.recordSlash(slashEntry); + + expect(prisma.slashLedger.create).toHaveBeenCalledWith({ + data: slashEntry, + }); + }); + }); + + describe("recordRefund", () => { + it("should record a refund in the ledger", async () => { + const refundEntry = { + intentId: "intent-123", + userAddress: "user-123", + asset: "native", + amount: "2000000", + issuedAt: new Date(), + reason: "Failed transaction", + txHash: "tx-hash", + }; + + prisma.refundLedger.create.mockResolvedValue({ + id: 1n, + ...refundEntry, + } as any); + + await service.recordRefund(refundEntry); + + expect(prisma.refundLedger.create).toHaveBeenCalledWith({ + data: refundEntry, + }); + }); + }); + + describe("calculateExpectedBalance", () => { + it("should calculate expected balance from ledgers", async () => { + const asset = "native"; + const now = new Date(); + + prisma.feeLedger.findMany.mockResolvedValue([ + { id: 1n, intentId: "i1", asset, amount: "1000000", accrualAt: now, txHash: null }, + { id: 2n, intentId: "i2", asset, amount: "2000000", accrualAt: now, txHash: null }, + ] as any); + + prisma.slashLedger.findMany.mockResolvedValue([ + { + id: 1n, + solverAddress: "s1", + asset, + amount: "500000", + slashedAt: now, + reason: "test", + txHash: null, + }, + ] as any); + + prisma.refundLedger.findMany.mockResolvedValue([ + { + id: 1n, + intentId: "i3", + userAddress: "u1", + asset, + amount: "300000", + issuedAt: now, + reason: "test", + txHash: null, + }, + ] as any); + + const result = await service.calculateExpectedBalance(asset); + + // 1000000 + 2000000 + 500000 - 300000 = 3200000 + expect(result).toEqual({ + asset: "native", + totalFees: "3000000", + totalSlashes: "500000", + totalRefunds: "300000", + netExpected: "3200000", + }); + }); + + it("should handle empty ledgers", async () => { + const asset = "USDC"; + + prisma.feeLedger.findMany.mockResolvedValue([]); + prisma.slashLedger.findMany.mockResolvedValue([]); + prisma.refundLedger.findMany.mockResolvedValue([]); + + const result = await service.calculateExpectedBalance(asset); + + expect(result).toEqual({ + asset: "USDC", + totalFees: "0", + totalSlashes: "0", + totalRefunds: "0", + netExpected: "0", + }); + }); + }); + + describe("reconcileAsset", () => { + it("should reconcile asset and detect no discrepancy", async () => { + const asset = "native"; + const date = new Date("2026-09-28"); + const expectedBalance = "10000000"; // 1 XLM + + // Mock expected balance calculation + prisma.feeLedger.findMany.mockResolvedValue([ + { + id: 1n, + intentId: "i1", + asset, + amount: expectedBalance, + accrualAt: date, + txHash: null, + }, + ] as any); + prisma.slashLedger.findMany.mockResolvedValue([]); + prisma.refundLedger.findMany.mockResolvedValue([]); + + // Mock actual balance fetch + jest.spyOn(service as any, "fetchActualBalance").mockResolvedValue({ + asset, + balance: expectedBalance, + }); + + prisma.treasurySnapshot.upsert.mockResolvedValue({} as any); + + const result = await service.reconcileAsset(asset, date); + + expect(result).toMatchObject({ + snapshotDate: "2026-09-28", + asset: "native", + expectedBalance: expectedBalance, + actualBalance: expectedBalance, + discrepancy: "0", + hasUnexplainedDiscrepancy: false, + }); + }); + + it("should detect discrepancy within tolerance", async () => { + const asset = "native"; + const date = new Date("2026-09-28"); + const expectedBalance = "100000000"; // 10 XLM + const actualBalance = "100500000"; // 10.05 XLM (0.5% higher) + + prisma.feeLedger.findMany.mockResolvedValue([ + { + id: 1n, + intentId: "i1", + asset, + amount: expectedBalance, + accrualAt: date, + txHash: null, + }, + ] as any); + prisma.slashLedger.findMany.mockResolvedValue([]); + prisma.refundLedger.findMany.mockResolvedValue([]); + + jest.spyOn(service as any, "fetchActualBalance").mockResolvedValue({ + asset, + balance: actualBalance, + }); + + prisma.treasurySnapshot.upsert.mockResolvedValue({} as any); + + const result = await service.reconcileAsset(asset, date); + + // Discrepancy is 500000 stroops (0.05 XLM), which is < tolerance of 10000000 (1 XLM) + expect(result).toMatchObject({ + asset: "native", + discrepancy: "500000", + hasUnexplainedDiscrepancy: false, + }); + }); + + it("should detect unexplained discrepancy exceeding tolerance", async () => { + const asset = "native"; + const date = new Date("2026-09-28"); + const expectedBalance = "100000000"; // 10 XLM + const actualBalance = "150000000"; // 15 XLM (5 XLM higher, exceeds 1 XLM tolerance) + + prisma.feeLedger.findMany.mockResolvedValue([ + { + id: 1n, + intentId: "i1", + asset, + amount: expectedBalance, + accrualAt: date, + txHash: null, + }, + ] as any); + prisma.slashLedger.findMany.mockResolvedValue([]); + prisma.refundLedger.findMany.mockResolvedValue([]); + + jest.spyOn(service as any, "fetchActualBalance").mockResolvedValue({ + asset, + balance: actualBalance, + }); + + prisma.treasurySnapshot.upsert.mockResolvedValue({} as any); + + const alertSpy = jest.spyOn(service as any, "alertDiscrepancy").mockResolvedValue(undefined); + + const result = await service.reconcileAsset(asset, date); + + expect(result).toMatchObject({ + asset: "native", + discrepancy: "50000000", + hasUnexplainedDiscrepancy: true, + }); + + expect(alertSpy).toHaveBeenCalled(); + }); + + it("should handle negative discrepancy (actual < expected)", async () => { + const asset = "native"; + const date = new Date("2026-09-28"); + const expectedBalance = "100000000"; // 10 XLM + const actualBalance = "50000000"; // 5 XLM (5 XLM lower) + + prisma.feeLedger.findMany.mockResolvedValue([ + { + id: 1n, + intentId: "i1", + asset, + amount: expectedBalance, + accrualAt: date, + txHash: null, + }, + ] as any); + prisma.slashLedger.findMany.mockResolvedValue([]); + prisma.refundLedger.findMany.mockResolvedValue([]); + + jest.spyOn(service as any, "fetchActualBalance").mockResolvedValue({ + asset, + balance: actualBalance, + }); + + prisma.treasurySnapshot.upsert.mockResolvedValue({} as any); + + const result = await service.reconcileAsset(asset, date); + + expect(result).toMatchObject({ + asset: "native", + discrepancy: "-50000000", + hasUnexplainedDiscrepancy: true, + }); + }); + }); + + describe("getReconciliationSummary", () => { + it("should return reconciliation summary for a date", async () => { + const snapshotDate = "2026-09-28"; + const mockSnapshots = [ + { + id: 1n, + snapshotDate, + asset: "native", + expectedBalance: "100000000", + actualBalance: "100000000", + discrepancy: "0", + toleranceThreshold: "10000000", + hasUnexplainedDiscrepancy: false, + explanation: "Balances match exactly.", + createdAt: new Date("2026-09-28T00:00:00Z"), + breakdown: { + fees: "100000000", + slashes: "0", + refunds: "0", + }, + }, + { + id: 2n, + snapshotDate, + asset: "USDC", + expectedBalance: "50000000", + actualBalance: "52000000", + discrepancy: "2000000", + toleranceThreshold: "1000000", + hasUnexplainedDiscrepancy: true, + explanation: "Exceeds tolerance.", + createdAt: new Date("2026-09-28T00:00:00Z"), + breakdown: { + fees: "50000000", + slashes: "0", + refunds: "0", + }, + }, + ]; + + prisma.treasurySnapshot.findMany.mockResolvedValue(mockSnapshots as any); + + const summary = await service.getReconciliationSummary(snapshotDate); + + expect(summary).toMatchObject({ + date: snapshotDate, + totalDiscrepancies: 1, + assetsWithUnexplainedDiscrepancies: 1, + }); + + expect(summary.assets).toHaveLength(2); + expect(summary.assets[0].asset).toBe("native"); + expect(summary.assets[1].asset).toBe("USDC"); + }); + }); + + describe("getReconciliationDetail", () => { + it("should return detailed reconciliation for an asset", async () => { + const snapshotDate = "2026-09-28"; + const asset = "native"; + + const mockSnapshot = { + id: 1n, + snapshotDate, + asset, + expectedBalance: "100000000", + actualBalance: "100500000", + discrepancy: "500000", + toleranceThreshold: "10000000", + hasUnexplainedDiscrepancy: false, + explanation: "Within tolerance.", + createdAt: new Date("2026-09-28T00:00:00Z"), + breakdown: { + fees: "100000000", + slashes: "0", + refunds: "0", + }, + }; + + prisma.treasurySnapshot.findUnique.mockResolvedValue(mockSnapshot as any); + + const mockFees = [ + { + id: 1n, + intentId: "i1", + asset, + amount: "50000000", + accrualAt: new Date("2026-09-27T12:00:00Z"), + txHash: "tx1", + }, + ]; + + const mockSlashes = [ + { + id: 1n, + solverAddress: "s1", + asset, + amount: "10000000", + slashedAt: new Date("2026-09-27T13:00:00Z"), + reason: "Timeout", + txHash: "tx2", + }, + ]; + + const mockRefunds = [ + { + id: 1n, + intentId: "i2", + userAddress: "u1", + asset, + amount: "5000000", + issuedAt: new Date("2026-09-27T14:00:00Z"), + reason: "Failed tx", + txHash: "tx3", + }, + ]; + + prisma.feeLedger.findMany.mockResolvedValue(mockFees as any); + prisma.slashLedger.findMany.mockResolvedValue(mockSlashes as any); + prisma.refundLedger.findMany.mockResolvedValue(mockRefunds as any); + + const detail = await service.getReconciliationDetail(asset, snapshotDate); + + expect(detail).toMatchObject({ + snapshotDate, + asset, + expectedBalance: "100000000", + actualBalance: "100500000", + discrepancy: "500000", + }); + + expect(detail.recentTransactions).toHaveLength(3); + expect(detail.recentTransactions[0].type).toBe("refund"); + expect(detail.recentTransactions[1].type).toBe("slash"); + expect(detail.recentTransactions[2].type).toBe("fee"); + }); + + it("should throw error if snapshot not found", async () => { + prisma.treasurySnapshot.findUnique.mockResolvedValue(null); + + await expect( + service.getReconciliationDetail("nonexistent", "2026-09-28"), + ).rejects.toThrow("No reconciliation found"); + }); + }); +}); diff --git a/src/treasury/treasury.service.ts b/src/treasury/treasury.service.ts index e69de29b..ea7d4c86 100644 --- a/src/treasury/treasury.service.ts +++ b/src/treasury/treasury.service.ts @@ -0,0 +1,538 @@ +import { Injectable, Logger } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Cron, CronExpression } from "@nestjs/schedule"; +import { PrismaService } from "../prisma/prisma.service"; +import { SorobanService } from "../soroban/soroban.service"; +import * as StellarSdk from "@stellar/stellar-sdk"; +import { + AssetBalance, + ExpectedBalance, + ReconciliationResult, + ReconciliationSummary, + ReconciliationDetailResponse, + FeeLedgerEntry, + SlashLedgerEntry, + RefundLedgerEntry, +} from "./treasury.types"; +import { AppConfig } from "../config/configuration"; + +/** + * TreasuryService + * + * Aggregates fee-ledger accruals, slash proceeds, and refunds, + * and reconciles them daily against actual on-chain treasury balances. + */ +@Injectable() +export class TreasuryService { + private readonly logger = new Logger(TreasuryService.name); + private readonly horizonServer: StellarSdk.Horizon.Server; + private readonly treasuryAddress: string; + private readonly toleranceThresholds: Map; + + constructor( + private readonly prisma: PrismaService, + private readonly soroban: SorobanService, + private readonly configService: ConfigService, + ) { + const horizonUrl = this.configService.get("stellar.horizonUrl", { infer: true }); + this.horizonServer = new StellarSdk.Horizon.Server(horizonUrl); + + this.treasuryAddress = this.configService.get("treasury.address", { infer: true }); + + // Default tolerance thresholds per asset (in base units) + // Could be moved to config/database + this.toleranceThresholds = new Map([ + ["native", 10000000n], // 1 XLM (7 decimals) + ["USDC", 1000000n], // 1 USDC (6 decimals) + ]); + } + + /** + * Record a fee accrual in the ledger + */ + async recordFee(entry: FeeLedgerEntry): Promise { + await this.prisma.feeLedger.create({ + data: { + intentId: entry.intentId, + asset: entry.asset, + amount: entry.amount, + accrualAt: entry.accrualAt, + txHash: entry.txHash, + }, + }); + + this.logger.log(`Recorded fee: ${entry.amount} ${entry.asset} for intent ${entry.intentId}`); + } + + /** + * Record a slash in the ledger + */ + async recordSlash(entry: SlashLedgerEntry): Promise { + await this.prisma.slashLedger.create({ + data: { + solverAddress: entry.solverAddress, + asset: entry.asset, + amount: entry.amount, + slashedAt: entry.slashedAt, + reason: entry.reason, + txHash: entry.txHash, + }, + }); + + this.logger.log(`Recorded slash: ${entry.amount} ${entry.asset} from solver ${entry.solverAddress}`); + } + + /** + * Record a refund in the ledger + */ + async recordRefund(entry: RefundLedgerEntry): Promise { + await this.prisma.refundLedger.create({ + data: { + intentId: entry.intentId, + userAddress: entry.userAddress, + asset: entry.asset, + amount: entry.amount, + issuedAt: entry.issuedAt, + reason: entry.reason, + txHash: entry.txHash, + }, + }); + + this.logger.log(`Recorded refund: ${entry.amount} ${entry.asset} to user ${entry.userAddress}`); + } + + /** + * Calculate expected treasury balance from ledgers + */ + async calculateExpectedBalance(asset: string, untilDate?: Date): Promise { + const until = untilDate || new Date(); + + // Aggregate fees + const fees = await this.prisma.feeLedger.findMany({ + where: { + asset, + accrualAt: { lte: until }, + }, + }); + const totalFees = fees.reduce((sum, f) => sum + BigInt(f.amount), 0n); + + // Aggregate slashes + const slashes = await this.prisma.slashLedger.findMany({ + where: { + asset, + slashedAt: { lte: until }, + }, + }); + const totalSlashes = slashes.reduce((sum, s) => sum + BigInt(s.amount), 0n); + + // Aggregate refunds + const refunds = await this.prisma.refundLedger.findMany({ + where: { + asset, + issuedAt: { lte: until }, + }, + }); + const totalRefunds = refunds.reduce((sum, r) => sum + BigInt(r.amount), 0n); + + const netExpected = totalFees + totalSlashes - totalRefunds; + + return { + asset, + totalFees: totalFees.toString(), + totalSlashes: totalSlashes.toString(), + totalRefunds: totalRefunds.toString(), + netExpected: netExpected.toString(), + }; + } + + /** + * Fetch actual on-chain balance for treasury account + */ + async fetchActualBalance(asset: string): Promise { + try { + const account = await this.horizonServer.loadAccount(this.treasuryAddress); + + // Handle native XLM + if (asset === "native") { + const balance = account.balances.find((b) => b.asset_type === "native"); + return { + asset: "native", + balance: balance ? this.parseBalance(balance.balance) : "0", + }; + } + + // Handle issued assets (traditional Stellar assets) + const [code, issuer] = asset.split(":"); + if (issuer) { + const balance = account.balances.find( + (b) => + b.asset_type !== "native" && + "asset_code" in b && + "asset_issuer" in b && + (b): b is typeof b & { asset_code: string; asset_issuer: string } => + b.asset_type !== "native" && + "asset_code" in b && + b.asset_code === code && + b.asset_issuer === issuer, + ); + return { + asset, + balance: balance ? this.parseBalance(balance.balance) : "0", + }; + } + + // Handle Soroban tokens (SAC balances) + // This would require calling a Soroban contract method + // For now, return placeholder - implement based on your contract structure + this.logger.warn(`Soroban asset balance fetch not yet implemented for ${asset}`); + return { + asset, + balance: "0", + contract: asset, + }; + } catch (error) { + this.logger.error(`Failed to fetch balance for ${asset}:`, error); + throw error; + } + } + + /** + * Parse Horizon balance string to base units (stroops) + */ + private parseBalance(balance: string): string { + // Horizon returns balances as decimal strings like "100.0000000" + // Convert to stroops (1 XLM = 10^7 stroops) + const [whole, decimal = ""] = balance.split("."); + const paddedDecimal = decimal.padEnd(7, "0"); + return (BigInt(whole) * 10000000n + BigInt(paddedDecimal)).toString(); + } + + /** + * Perform reconciliation for a single asset + */ + async reconcileAsset( + asset: string, + date: Date = new Date(), + ): Promise { + const snapshotDate = date.toISOString().split("T")[0]; + + this.logger.log(`Reconciling asset ${asset} for date ${snapshotDate}`); + + // Calculate expected balance from ledgers + const expected = await this.calculateExpectedBalance(asset, date); + + // Fetch actual on-chain balance + const actual = await this.fetchActualBalance(asset); + + const expectedBigInt = BigInt(expected.netExpected); + const actualBigInt = BigInt(actual.balance); + const discrepancy = actualBigInt - expectedBigInt; + const absDiscrepancy = discrepancy < 0n ? -discrepancy : discrepancy; + + const tolerance = this.toleranceThresholds.get(asset) || 0n; + const hasUnexplainedDiscrepancy = absDiscrepancy > tolerance; + + // Calculate percentage + const discrepancyPercentage = expectedBigInt > 0n + ? Number((discrepancy * 10000n) / expectedBigInt) / 100 + : 0; + + // Generate explanation + const explanation = this.generateExplanation( + discrepancy, + hasUnexplainedDiscrepancy, + asset, + ); + + const result: ReconciliationResult = { + snapshotDate, + asset, + expectedBalance: expected.netExpected, + actualBalance: actual.balance, + discrepancy: discrepancy.toString(), + discrepancyPercentage, + toleranceThreshold: tolerance.toString(), + hasUnexplainedDiscrepancy, + explanation, + breakdown: { + fees: expected.totalFees, + slashes: expected.totalSlashes, + refunds: expected.totalRefunds, + }, + }; + + // Save snapshot to database + await this.prisma.treasurySnapshot.upsert({ + where: { + snapshot_date_asset_unique: { + snapshotDate, + asset, + }, + }, + create: { + snapshotDate, + asset, + expectedBalance: expected.netExpected, + actualBalance: actual.balance, + discrepancy: discrepancy.toString(), + toleranceThreshold: tolerance.toString(), + hasUnexplainedDiscrepancy, + explanation, + breakdown: result.breakdown, + }, + update: { + expectedBalance: expected.netExpected, + actualBalance: actual.balance, + discrepancy: discrepancy.toString(), + hasUnexplainedDiscrepancy, + explanation, + breakdown: result.breakdown, + }, + }); + + // Alert on unexplained discrepancies + if (hasUnexplainedDiscrepancy) { + await this.alertDiscrepancy(result); + } + + return result; + } + + /** + * Generate human-readable explanation for discrepancies + */ + private generateExplanation( + discrepancy: bigint, + hasUnexplainedDiscrepancy: boolean, + asset: string, + ): string | null { + if (discrepancy === 0n) { + return "Balances match exactly."; + } + + if (!hasUnexplainedDiscrepancy) { + return `Discrepancy within tolerance threshold. Likely due to in-flight settlements or pending transactions.`; + } + + const direction = discrepancy > 0n ? "higher" : "lower"; + return `Treasury balance is ${direction} than expected by ${discrepancy.toString()} base units. This exceeds the tolerance threshold and requires investigation.`; + } + + /** + * Perform daily reconciliation for all tracked assets + */ + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) + async performDailyReconciliation(): Promise { + this.logger.log("Starting daily treasury reconciliation"); + + try { + // Get all unique assets from ledgers + const assetsFromFees = await this.prisma.feeLedger.findMany({ + select: { asset: true }, + distinct: ["asset"], + }); + + const assetsFromSlashes = await this.prisma.slashLedger.findMany({ + select: { asset: true }, + distinct: ["asset"], + }); + + const allAssets = new Set([ + ...assetsFromFees.map((f) => f.asset), + ...assetsFromSlashes.map((s) => s.asset), + ]); + + const results: ReconciliationResult[] = []; + + for (const asset of allAssets) { + try { + const result = await this.reconcileAsset(asset); + results.push(result); + } catch (error) { + this.logger.error(`Failed to reconcile asset ${asset}:`, error); + } + } + + const withDiscrepancies = results.filter((r) => r.hasUnexplainedDiscrepancy); + + this.logger.log( + `Daily reconciliation complete. ${results.length} assets checked, ` + + `${withDiscrepancies.length} with unexplained discrepancies.`, + ); + } catch (error) { + this.logger.error("Daily reconciliation failed:", error); + throw error; + } + } + + /** + * Get reconciliation summary for a specific date + */ + async getReconciliationSummary(date?: string): Promise { + const snapshotDate = date || new Date().toISOString().split("T")[0]; + + const snapshots = await this.prisma.treasurySnapshot.findMany({ + where: { snapshotDate }, + orderBy: { asset: "asc" }, + }); + + const assets: ReconciliationResult[] = snapshots.map((s) => ({ + snapshotDate: s.snapshotDate, + asset: s.asset, + expectedBalance: s.expectedBalance, + actualBalance: s.actualBalance, + discrepancy: s.discrepancy, + discrepancyPercentage: this.calculatePercentage( + BigInt(s.discrepancy), + BigInt(s.expectedBalance), + ), + toleranceThreshold: s.toleranceThreshold, + hasUnexplainedDiscrepancy: s.hasUnexplainedDiscrepancy, + explanation: s.explanation, + breakdown: s.breakdown as any, + })); + + return { + date: snapshotDate, + assets, + totalDiscrepancies: assets.filter((a) => BigInt(a.discrepancy) !== 0n).length, + assetsWithUnexplainedDiscrepancies: assets.filter( + (a) => a.hasUnexplainedDiscrepancy, + ).length, + lastReconciliationAt: snapshots[0]?.createdAt.toISOString() || new Date().toISOString(), + }; + } + + /** + * Get detailed reconciliation for a specific asset + */ + async getReconciliationDetail( + asset: string, + date?: string, + ): Promise { + const snapshotDate = date || new Date().toISOString().split("T")[0]; + + const snapshot = await this.prisma.treasurySnapshot.findUnique({ + where: { + snapshot_date_asset_unique: { + snapshotDate, + asset, + }, + }, + }); + + if (!snapshot) { + throw new Error(`No reconciliation found for asset ${asset} on ${snapshotDate}`); + } + + // Fetch recent transactions (last 100 of each type) + const [fees, slashes, refunds] = await Promise.all([ + this.prisma.feeLedger.findMany({ + where: { asset }, + orderBy: { accrualAt: "desc" }, + take: 100, + }), + this.prisma.slashLedger.findMany({ + where: { asset }, + orderBy: { slashedAt: "desc" }, + take: 100, + }), + this.prisma.refundLedger.findMany({ + where: { asset }, + orderBy: { issuedAt: "desc" }, + take: 100, + }), + ]); + + const recentTransactions = [ + ...fees.map((f) => ({ + type: "fee" as const, + amount: f.amount, + timestamp: f.accrualAt.toISOString(), + reference: f.intentId, + })), + ...slashes.map((s) => ({ + type: "slash" as const, + amount: s.amount, + timestamp: s.slashedAt.toISOString(), + reference: s.solverAddress, + })), + ...refunds.map((r) => ({ + type: "refund" as const, + amount: r.amount, + timestamp: r.issuedAt.toISOString(), + reference: r.intentId, + })), + ].sort((a, b) => b.timestamp.localeCompare(a.timestamp)); + + return { + snapshotDate: snapshot.snapshotDate, + asset: snapshot.asset, + expectedBalance: snapshot.expectedBalance, + actualBalance: snapshot.actualBalance, + discrepancy: snapshot.discrepancy, + discrepancyPercentage: this.calculatePercentage( + BigInt(snapshot.discrepancy), + BigInt(snapshot.expectedBalance), + ), + toleranceThreshold: snapshot.toleranceThreshold, + hasUnexplainedDiscrepancy: snapshot.hasUnexplainedDiscrepancy, + explanation: snapshot.explanation, + breakdown: snapshot.breakdown as any, + recentTransactions, + }; + } + + /** + * Calculate percentage from bigints + */ + private calculatePercentage(discrepancy: bigint, expected: bigint): number { + if (expected === 0n) return 0; + return Number((discrepancy * 10000n) / expected) / 100; + } + + /** + * Alert on unexplained discrepancies + */ + private async alertDiscrepancy(result: ReconciliationResult): Promise { + const severity = this.getSeverity(result); + + this.logger.warn( + `[${severity.toUpperCase()}] Treasury discrepancy detected for ${result.asset}: ` + + `${result.discrepancy} base units (${result.discrepancyPercentage.toFixed(2)}%)`, + ); + + // TODO: Integrate with alerting system (PagerDuty, Slack, etc.) + // For now, just log the alert + } + + /** + * Determine severity of discrepancy + */ + private getSeverity(result: ReconciliationResult): "warning" | "critical" { + const absPercentage = Math.abs(result.discrepancyPercentage); + + // Critical if discrepancy > 5% + if (absPercentage > 5) { + return "critical"; + } + + return "warning"; + } + + /** + * Manual reconciliation trigger (admin use) + */ + async triggerReconciliation(asset?: string): Promise { + if (asset) { + const result = await this.reconcileAsset(asset); + return [result]; + } + + // Reconcile all assets + await this.performDailyReconciliation(); + + const summary = await this.getReconciliationSummary(); + return summary.assets; + } +} diff --git a/test/__mocks__/@stellar/stellar-sdk.ts b/test/__mocks__/@stellar/stellar-sdk.ts index e69de29b..c830c445 100644 --- a/test/__mocks__/@stellar/stellar-sdk.ts +++ b/test/__mocks__/@stellar/stellar-sdk.ts @@ -0,0 +1,119 @@ +/** + * Hermetic test double for `@stellar/stellar-sdk`. + * + * The e2e suite must not talk to a real Soroban RPC node, but it *does* need + * the genuine SDK for everything that is pure computation: Ed25519 keypairs + * and signature verification (`Keypair`, `verifyStellarSignature`), Stellar + * strkey encode/decode (`StrKey`, `Address`), XDR marshalling (`xdr`, + * `nativeToScVal`, `scValToNative`) and transaction assembly + * (`TransactionBuilder`, `Contract`). Re-implementing those by hand would let + * tests pass against a fake crypto path that production never uses. + * + * So this mock re-exports the real module and replaces *only* the network + * layer — `SorobanRpc.Server` — with an in-memory stub. + * + * Resolution note: jest maps the bare specifier `@stellar/stellar-sdk` to this + * file, so requiring the bare specifier here would recurse forever. The real + * module is therefore loaded by filesystem path, which the + * `moduleNameMapper` regex (`^@stellar/stellar-sdk$`, anchored) does not match. + * A `require`-based load also sidesteps the package's `exports` gate, which + * only permits `.`, `./contract`, and `./rpc`. + */ + +/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */ +import * as path from "node:path"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const real: any = require( + // __dirname is /test/__mocks__/@stellar, so the repo root is two levels + // up ("../..") — three levels overshoots the repo and makes `real` undefined, + // which surfaces far away as "Cannot read properties of undefined (reading + // 'Server')" inside every e2e suite that boots the Nest app. + path.join(__dirname, "..", "..", "..", "node_modules", "@stellar", "stellar-sdk", "lib", "index.js"), +); + +const mockServer = { + getHealth: jest.fn().mockResolvedValue({ status: "ok" }), + getLatestLedger: jest.fn().mockResolvedValue({ sequence: 1 }), + getNetwork: jest.fn().mockResolvedValue({ passphrase: "test" }), + getAccount: jest.fn().mockResolvedValue({ id: "test", sequenceNumber: () => "0" }), + getEvents: jest.fn().mockResolvedValue({ events: [], latestLedger: 1 }), + getFeeStats: jest.fn().mockResolvedValue({ + sorobanInclusionFee: { + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + max: "100", + }, + }), + simulateTransaction: jest.fn().mockResolvedValue({ minResourceFee: "100" }), + prepareTransaction: jest.fn().mockImplementation((tx: unknown) => tx), + sendTransaction: jest.fn().mockResolvedValue({ status: "SUCCESS", hash: "mock-hash" }), +}; + +const mockServerClass = jest.fn().mockImplementation(() => mockServer); + +/** + * Network stub. `Api` is spread from the real module so type guards such as + * `SorobanRpc.Api.isSimulationError` keep working exactly as in production. + */ +export const SorobanRpc = { + ...real.SorobanRpc, + Server: mockServerClass, + Api: { + ...real.SorobanRpc?.Api, + isSimulationError: (response: unknown): boolean => + Boolean( + response && + typeof response === "object" && + "error" in (response as Record) && + (response as Record).error != null, + ), + }, +}; + +// ── Genuine SDK re-exports ─────────────────────────────────────────────────── +// Everything below is the real implementation, re-exported explicitly rather +// than via `export *` so that the star-export does not shadow the stubbed +// `SorobanRpc` above and so each name is individually type-checked. + +export const Keypair = real.Keypair; +export const Networks = real.Networks; +export const StrKey = real.StrKey; +export const Address = real.Address; +export const Asset = real.Asset; +export const Horizon = real.Horizon; +export const Contract = real.Contract; +export const Account = real.Account; +export const Operation = real.Operation; +export const Transaction = real.Transaction; +export const FeeBumpTransaction = real.FeeBumpTransaction; +export const TransactionBuilder = real.TransactionBuilder; +export const xdr = real.xdr; +export const nativeToScVal = real.nativeToScVal; +export const scValToNative = real.scValToNative; +export const BASE_FEE = real.BASE_FEE; +// Namespaced clients used by production code (e.g. TreasuryService builds +// `new StellarSdk.Horizon.Server(...)`). Without these the mock leaves +// `StellarSdk.Horizon` undefined and every e2e suite that boots the app fails. +export const Horizon = real.Horizon; +export const Utils = real.Utils; +export const Config = real.Config; +export const MuxedAccount = real.MuxedAccount; +export const hash = real.hash; +export const Memo = real.Memo; +export const Timepoint = real.Timepoint; +export const SorobanDataBuilder = real.SorobanDataBuilder; +export const authorizeEntry = real.authorizeEntry; +export const decodeAddressToScVal = real.decodeAddressToScVal; +export const encodeAddressToScVal = real.encodeAddressToScVal; diff --git a/test/cors.e2e-spec.ts b/test/cors.e2e-spec.ts index e69de29b..5d8eb760 100644 --- a/test/cors.e2e-spec.ts +++ b/test/cors.e2e-spec.ts @@ -0,0 +1,192 @@ +/** + * e2e test: CORS_ORIGIN config is wired into the app. + * + * Verifies that Access-Control-Allow-Origin reflects the CORS_ORIGIN env var + * rather than being absent (NestJS default) or hard-coded. + */ +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { Test } from "@nestjs/testing"; +import { WsAdapter } from "@nestjs/platform-ws"; +import { ConfigService } from "@nestjs/config"; +import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import helmet from "helmet"; +import request from "supertest"; +import { AppModule } from "../src/app.module"; +import { AppConfig } from "../src/config/configuration"; +import { HttpExceptionFilter } from "../src/common/http-exception.filter"; +import { PrismaService } from "../src/prisma/prisma.service"; +import { MockPrismaService } from "./utils/create-test-app"; + +async function createAppWithOrigin(origin: string): Promise { + // Override CORS_ORIGIN before the module initializes. + process.env.CORS_ORIGIN = origin; + + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(PrismaService) + .useClass(MockPrismaService) + .compile(); + + const app = moduleRef.createNestApplication(); + app.useWebSocketAdapter(new WsAdapter(app)); + app.useGlobalFilters(new HttpExceptionFilter()); + app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); + + const configService = app.get(ConfigService); + const corsOrigin = configService.get("corsOrigin", { infer: true }); + app.enableCors({ origin: corsOrigin }); + + await app.init(); + return app; +} + +async function createAppWithSecurityHeaders(nodeEnv = "development"): Promise { + const previousNodeEnv = process.env.NODE_ENV; + const previousAllowLocalSigner = process.env.ALLOW_LOCAL_SIGNER_IN_PROD; + process.env.NODE_ENV = nodeEnv; + // The app refuses to boot in production with the local keypair signer, which + // is the right production guard but unrelated to the security headers and + // Swagger-visibility behaviour this suite exercises. Opt in for the boot only. + if (nodeEnv === "production") process.env.ALLOW_LOCAL_SIGNER_IN_PROD = "true"; + + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideProvider(PrismaService) + .useClass(MockPrismaService) + .compile(); + + const app = moduleRef.createNestApplication(); + app.set("trust proxy", 1); + app.use((req, res, next) => { + const isDocsRequest = req.path === "/docs" || req.path === "/docs-json"; + if (isDocsRequest) { + res.setHeader("Cache-Control", "no-store"); + } + next(); + }); + app.use( + helmet({ + hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }, + frameguard: { action: "deny" }, + referrerPolicy: { policy: "strict-origin-when-cross-origin" }, + noSniff: true, + }), + ); + app.useWebSocketAdapter(new WsAdapter(app)); + app.useGlobalFilters(new HttpExceptionFilter()); + app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); + + // Mirror main.ts: serve the OpenAPI document, and only mount the Swagger UI + // outside production. Without this the /docs and /docs-json assertions below + // would 404 regardless of the security headers under test. + const swaggerConfig = new DocumentBuilder() + .setTitle("Vortex Backend") + .setDescription("Intent relay API + WebSocket feed for Vortex Protocol") + .setVersion("0.1.0") + .build(); + const document = SwaggerModule.createDocument(app, swaggerConfig); + if (nodeEnv !== "production") { + SwaggerModule.setup("docs", app, document); + } + + await app.init(); + + process.env.NODE_ENV = previousNodeEnv; + if (previousAllowLocalSigner === undefined) { + delete process.env.ALLOW_LOCAL_SIGNER_IN_PROD; + } else { + process.env.ALLOW_LOCAL_SIGNER_IN_PROD = previousAllowLocalSigner; + } + return app; +} + +describe("CORS (e2e)", () => { + afterEach(() => { + // Restore so other tests are not affected. + delete process.env.CORS_ORIGIN; + }); + + it("responds with Access-Control-Allow-Origin: * when CORS_ORIGIN is *", async () => { + const app = await createAppWithOrigin("*"); + try { + const res = await request(app.getHttpServer()) + .get("/health") + .set("Origin", "http://example.com") + .expect(200); + + expect(res.headers["access-control-allow-origin"]).toBe("*"); + } finally { + await app.close(); + } + }); + + it("reflects a specific CORS_ORIGIN in the response header", async () => { + const allowedOrigin = "https://app.vortex.finance"; + const app = await createAppWithOrigin(allowedOrigin); + try { + const res = await request(app.getHttpServer()) + .get("/health") + .set("Origin", allowedOrigin) + .expect(200); + + expect(res.headers["access-control-allow-origin"]).toBe(allowedOrigin); + } finally { + await app.close(); + } + }); + + it("does NOT echo back an origin that is not in CORS_ORIGIN", async () => { + const app = await createAppWithOrigin("https://app.vortex.finance"); + try { + const res = await request(app.getHttpServer()) + .get("/health") + .set("Origin", "https://evil.example.com") + .expect(200); + + // When origin is a specific string and the request Origin doesn't match, + // express-cors either omits the header or sets it to the allowed origin. + // Either way it must NOT be the attacker's origin. + expect(res.headers["access-control-allow-origin"]).not.toBe("https://evil.example.com"); + } finally { + await app.close(); + } + }); + + it("adds HSTS and nosniff headers behind a trusted proxy", async () => { + const app = await createAppWithSecurityHeaders(); + try { + const res = await request(app.getHttpServer()) + .get("/health") + .set("X-Forwarded-Proto", "https") + .expect(200); + + expect(res.headers["strict-transport-security"]).toContain("max-age=31536000"); + expect(res.headers["x-content-type-options"]).toBe("nosniff"); + expect(res.headers["x-frame-options"]).toBe("DENY"); + expect(res.headers["referrer-policy"]).toBe("strict-origin-when-cross-origin"); + } finally { + await app.close(); + } + }); + + it("marks OpenAPI docs as no-store so they are not cached", async () => { + const app = await createAppWithSecurityHeaders(); + try { + const res = await request(app.getHttpServer()).get("/docs-json").expect(200); + expect(res.headers["cache-control"]).toContain("no-store"); + } finally { + await app.close(); + } + }); + + it("disables Swagger UI by default in production", async () => { + const app = await createAppWithSecurityHeaders("production"); + try { + await request(app.getHttpServer()).get("/docs").expect(404); + } finally { + await app.close(); + } + }); +}); diff --git a/test/dos-limits.e2e-spec.ts b/test/dos-limits.e2e-spec.ts index e69de29b..1d92bb4c 100644 --- a/test/dos-limits.e2e-spec.ts +++ b/test/dos-limits.e2e-spec.ts @@ -0,0 +1,297 @@ +/** + * DoS / resource-exhaustion test suite (issue #476). + * + * Tests every limit added by the issue #476 hardening pass: + * + * 1. Body size cap — POST > 10 KB → 413 + * 2. JSON depth cap — body nesting > 10 deep → 400 + * 3. Batch size cap — POST /batch with > 100 IDs → 400 + * 4. Pagination cap — GET /intents?limit=101 → 400 + * 5. WS subscribe chain-filter cap — chains array > 20 → subscribe_rejected + * 6. WS subscription-count cap — > 10 subscribe messages → subscribe_rejected + * + * For each limit the test covers: + * - Just-under (or at) the limit → passes (2xx / subscribed) + * - Just-over the limit → rejected (4xx / subscribe_rejected) + */ +import { INestApplication } from "@nestjs/common"; +import request from "supertest"; +import WebSocket from "ws"; +import { createTestApp } from "./utils/create-test-app"; +import { + BATCH_LOOKUP_MAX_IDS, + JSON_MAX_DEPTH, + LIST_MAX_LIMIT, + WS_MAX_FILTER_CHAINS, + WS_MAX_SUBSCRIPTIONS_PER_CONNECTION, +} from "../src/config/limits.config"; + +/** Build a nested JSON object `depth` levels deep with a leaf value. */ +function buildNestedObject(depth: number, leaf: unknown = "leaf"): unknown { + if (depth <= 0) return leaf; + return { a: buildNestedObject(depth - 1, leaf) }; +} + +/** Wait for a WS message matching a predicate and resolve with parsed data. */ +function waitForMessage( + ws: WebSocket, + predicate: (msg: Record) => boolean, + timeoutMs = 3000, +): Promise> { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("waitForMessage timeout")), timeoutMs); + + ws.on("message", (data) => { + const msg = JSON.parse(data.toString()) as Record; + if (predicate(msg)) { + clearTimeout(timer); + resolve(msg); + } + }); + + ws.once("error", (err) => { + clearTimeout(timer); + reject(err); + }); + }); +} + +/** Open a WS connection, wait for the initial "connected" + "snapshot" pair, + * then return the client. Rejects if either message does not arrive. */ +async function openWs(port: number): Promise { + const ws = new WebSocket(`ws://localhost:${port}/ws`); + + await new Promise((resolve, reject) => { + ws.once("error", reject); + ws.once("open", () => { + // Drain the initial connected+snapshot before returning. + let seen = 0; + const handler = () => { + seen++; + if (seen >= 2) { + ws.removeListener("message", handler); + resolve(); + } + }; + ws.on("message", handler); + }); + }); + + return ws; +} + +// ───────────────────────────────────────────────────────────────────────────── + +describe("DoS / resource-exhaustion limits (issue #476)", () => { + let app: INestApplication; + let httpServer: ReturnType; + + beforeAll(async () => { + app = await createTestApp(); + // Bind an ephemeral port: the WS sections below read the real port off + // httpServer.address(), which is null for an initialised-but-not-listening + // app. Port 0 lets the OS pick, so parallel suites never collide. + await app.listen(0); + httpServer = app.getHttpServer(); + }); + + afterAll(async () => { + await app.close(); + }); + + // ── 1. Body size cap ───────────────────────────────────────────────────── + describe("1. Body size cap (10 KB)", () => { + it("rejects a payload > 10 KB with 413", async () => { + const oversized = { + srcAmount: "1".padEnd(11 * 1024, "0"), // clearly >10 KB + }; + await request(httpServer) + .post("/api/v1/intents") + .send(oversized) + .expect(413); + }); + + it("accepts a well-formed payload within 10 KB with 201/400 (not 413)", async () => { + // A valid-shaped but short body — validation may still 400 it for missing + // required fields, but it must NOT 413 (body was accepted by the size gate). + const small = { srcAmount: "1000000" }; + const res = await request(httpServer).post("/api/v1/intents").send(small); + expect(res.status).not.toBe(413); + }); + }); + + // ── 2. JSON depth cap ───────────────────────────────────────────────────── + describe(`2. JSON depth cap (max ${JSON_MAX_DEPTH})`, () => { + it(`rejects a body nested ${JSON_MAX_DEPTH + 1} levels deep with 400`, async () => { + const tooDeep = buildNestedObject(JSON_MAX_DEPTH + 1); + await request(httpServer) + .post("/api/v1/intents") + .send(tooDeep) + .expect(400); + }); + + it(`accepts a body nested exactly ${JSON_MAX_DEPTH} levels deep (not 400 from depth check)`, async () => { + // Build an object exactly at the depth limit — it should pass the depth + // guard even if it still fails DTO validation. + const atLimit = buildNestedObject(JSON_MAX_DEPTH); + const res = await request(httpServer).post("/api/v1/intents").send(atLimit); + // Must not be 400 due to depth — DTO validation may reject it for other reasons. + // We specifically check the response body does not mention "nesting depth". + if (res.status === 400) { + const body = res.body as { message?: string | string[] }; + const msgs = Array.isArray(body.message) + ? body.message.join(" ") + : body.message ?? ""; + expect(msgs).not.toContain("nesting depth"); + } + }); + + it("rejects a body nested 100 levels deep (well over the cap) with 400", async () => { + const wayTooDeep = buildNestedObject(100); + const res = await request(httpServer) + .post("/api/v1/intents") + .send(wayTooDeep) + .expect(400); + expect((res.body as { message?: string }).message).toMatch(/nesting depth/i); + }); + }); + + // ── 3. Batch size cap ──────────────────────────────────────────────────── + describe(`3. Batch size cap (max ${BATCH_LOOKUP_MAX_IDS} IDs)`, () => { + it(`rejects ${BATCH_LOOKUP_MAX_IDS + 1} IDs with 400`, async () => { + const oversized = { + intentIds: Array.from({ length: BATCH_LOOKUP_MAX_IDS + 1 }, (_, i) => `id-${i}`), + }; + await request(httpServer) + .post("/api/v1/intents/batch") + .send(oversized) + .expect(400); + }); + + it(`accepts exactly ${BATCH_LOOKUP_MAX_IDS} IDs with 200`, async () => { + const atLimit = { + intentIds: Array.from({ length: BATCH_LOOKUP_MAX_IDS }, (_, i) => `id-${i}`), + }; + // 200 expected — none of the IDs exist so the response will be an empty array. + await request(httpServer) + .post("/api/v1/intents/batch") + .send(atLimit) + .expect(200); + }); + + it("accepts 1 ID with 200", async () => { + await request(httpServer) + .post("/api/v1/intents/batch") + .send({ intentIds: ["does-not-exist"] }) + .expect(200); + }); + + it("rejects a non-array intentIds with 400", async () => { + await request(httpServer) + .post("/api/v1/intents/batch") + .send({ intentIds: "single-string-not-array" }) + .expect(400); + }); + }); + + // ── 4. Pagination limit cap ─────────────────────────────────────────────── + describe(`4. Pagination limit cap (max ${LIST_MAX_LIMIT})`, () => { + it(`rejects limit=${LIST_MAX_LIMIT + 1} with 400`, async () => { + await request(httpServer) + .get("/api/v1/intents") + .query({ limit: LIST_MAX_LIMIT + 1 }) + .expect(400); + }); + + it(`accepts limit=${LIST_MAX_LIMIT} with 200`, async () => { + await request(httpServer) + .get("/api/v1/intents") + .query({ limit: LIST_MAX_LIMIT }) + .expect(200); + }); + + it("accepts limit=1 with 200", async () => { + await request(httpServer).get("/api/v1/intents").query({ limit: 1 }).expect(200); + }); + + it("rejects limit=0 with 400", async () => { + await request(httpServer).get("/api/v1/intents").query({ limit: 0 }).expect(400); + }); + }); + + // ── 5. WS subscribe chain-filter cap ───────────────────────────────────── + describe(`5. WS subscribe chain-filter cap (max ${WS_MAX_FILTER_CHAINS} chains)`, () => { + it(`rejects a subscribe message with ${WS_MAX_FILTER_CHAINS + 1} chains`, (done) => { + const port = (httpServer.address() as { port: number }).port; + + openWs(port).then((ws) => { + const tooManyChains = Array.from( + { length: WS_MAX_FILTER_CHAINS + 1 }, + (_, i) => `chain-${i}`, + ); + + ws.send(JSON.stringify({ type: "subscribe", chains: tooManyChains })); + + waitForMessage(ws, (m) => m.type === "subscribe_rejected") + .then((msg) => { + expect(msg.reason).toBeDefined(); + ws.close(); + done(); + }) + .catch(done); + }).catch(done); + }); + + it(`accepts a subscribe message with exactly ${WS_MAX_FILTER_CHAINS} chains`, (done) => { + const port = (httpServer.address() as { port: number }).port; + + openWs(port).then((ws) => { + // Fill with the real supported chains (only 7 exist, so pad with repeats + // to reach exactly WS_MAX_FILTER_CHAINS — they will be validated against + // SUPPORTED_CHAINS and only valid ones kept, but the message itself should + // not be rejected at the length gate). + const chains = Array.from({ length: WS_MAX_FILTER_CHAINS }, (_, i) => + i % 2 === 0 ? "stellar" : "ethereum", + ); + + ws.send(JSON.stringify({ type: "subscribe", chains })); + + waitForMessage(ws, (m) => m.type === "subscribed" || m.type === "subscribe_rejected") + .then((msg) => { + // Should be "subscribed" (length gate passed), not "subscribe_rejected" + expect(msg.type).toBe("subscribed"); + ws.close(); + done(); + }) + .catch(done); + }).catch(done); + }); + }); + + // ── 6. WS subscription-count cap ───────────────────────────────────────── + describe(`6. WS subscription-count cap (max ${WS_MAX_SUBSCRIPTIONS_PER_CONNECTION} per connection)`, () => { + it(`rejects the ${WS_MAX_SUBSCRIPTIONS_PER_CONNECTION + 1}th subscribe message`, (done) => { + const port = (httpServer.address() as { port: number }).port; + + openWs(port).then(async (ws) => { + // Send exactly WS_MAX_SUBSCRIPTIONS_PER_CONNECTION subscribe messages; + // all should succeed. + for (let i = 0; i < WS_MAX_SUBSCRIPTIONS_PER_CONNECTION; i++) { + ws.send(JSON.stringify({ type: "subscribe", chains: ["stellar"] })); + await waitForMessage(ws, (m) => m.type === "subscribed" || m.type === "subscribe_rejected"); + } + + // The next one must be rejected. + ws.send(JSON.stringify({ type: "subscribe", chains: ["stellar"] })); + + waitForMessage(ws, (m) => m.type === "subscribe_rejected") + .then((msg) => { + expect(msg.reason).toBeDefined(); + ws.close(); + done(); + }) + .catch(done); + }).catch(done); + }, 15_000); + }); +}); diff --git a/test/jest-e2e.json b/test/jest-e2e.json index e69de29b..4416d593 100644 --- a/test/jest-e2e.json +++ b/test/jest-e2e.json @@ -0,0 +1,13 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": "..", + "testEnvironment": "node", + "testRegex": "test/.*\\.(e2e-spec|test)\\.ts$", + "transform": { + "^.+\\.ts$": ["ts-jest", { "diagnostics": false }] + }, + "moduleNameMapper": { + "^@stellar/stellar-sdk$": "/test/__mocks__/@stellar/stellar-sdk.ts", + "^@nestjs/schedule$": "/test/__mocks__/@nestjs/schedule.ts" + } +} diff --git a/test/load/concurrent-accept.test.ts b/test/load/concurrent-accept.test.ts index e69de29b..be3469be 100644 --- a/test/load/concurrent-accept.test.ts +++ b/test/load/concurrent-accept.test.ts @@ -0,0 +1,282 @@ +import { INestApplication } from "@nestjs/common"; +import request from "supertest"; +import { Keypair } from "@stellar/stellar-sdk"; +import { createTestApp } from "../utils/create-test-app"; +import { InMemoryIntentsRepository, isVersionConflict } from "../../src/intents/intents.repository"; +import { IntentsService } from "../../src/intents/intents.service"; +import { IntentsSweeperService } from "../../src/intents/intents-sweeper.service"; +import { SolverRegistryService } from "../../src/soroban/solver-registry.service"; +import { SEED_SOLVER_KEYPAIRS } from "../../src/solvers/solvers.seed"; +import { buildAcceptMessage, buildCancelMessage, buildFillMessage } from "../../src/common/stellar-signature"; + +const SOLVERS = [SEED_SOLVER_KEYPAIRS.ALPHA, SEED_SOLVER_KEYPAIRS.BETA, SEED_SOLVER_KEYPAIRS.GAMMA]; +const USER = Keypair.random(); + +function sign(kp: Keypair, message: string): string { + return kp.sign(Buffer.from(message, "utf8")).toString("base64"); +} + +const validCreateBody = { + user: USER.publicKey(), + srcChain: "ethereum", + srcTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + srcTokenSymbol: "USDC", + srcTokenDecimals: 6, + srcAmount: "1000000", + dstTokenContract: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA", + dstTokenSymbol: "USDC", + dstTokenDecimals: 7, + minDstAmount: "990000", +}; + +describe("Concurrent accept / fill race load test", () => { + let app: INestApplication; + let baseUrl: string; + + beforeAll(async () => { + app = await createTestApp(); + // Bind once: handing supertest an unbound server makes it open a new + // ephemeral listener per request, which resets under 20-way concurrency. + await app.listen(0, "127.0.0.1"); + baseUrl = (await app.getUrl()).replace("[::1]", "127.0.0.1"); + }); + + afterAll(async () => { + await app.close(); + }); + + const http = () => request(baseUrl); + + async function createIntent(): Promise { + const res = await http().post("/api/v1/intents").send(validCreateBody).expect(201); + return res.body.intentId as string; + } + + function accept(intentId: string, solver: Keypair) { + return http() + .post(`/api/v1/intents/${intentId}/accept`) + .send({ solver: solver.publicKey(), signature: sign(solver, buildAcceptMessage(intentId, solver.publicKey())) }); + } + + function fill(intentId: string, solver: Keypair) { + return http() + .post(`/api/v1/intents/${intentId}/fill`) + .send({ + solver: solver.publicKey(), + fillAmount: "995000", + txHash: `tx-${Math.random().toString(16).slice(2)}`, + signature: sign(solver, buildFillMessage(intentId, solver.publicKey())), + }); + } + + function cancel(intentId: string) { + return http() + .post(`/api/v1/intents/${intentId}/cancel`) + .send({ user: USER.publicKey(), signature: sign(USER, buildCancelMessage(intentId)) }); + } + + it("only one solver wins when N concurrent accept() calls race on the same intent", async () => { + const intentId = await createIntent(); + + const results = await Promise.all(Array.from({ length: 20 }, (_, i) => accept(intentId, SOLVERS[i % SOLVERS.length]))); + + expect(results.filter((r) => r.status === 201)).toHaveLength(1); + expect(results.filter((r) => r.status !== 201).every((r) => r.status === 409)).toBe(true); + + const intent = (await http().get(`/api/v1/intents/${intentId}`).expect(200)).body; + expect(intent.state).toBe("accepted"); + expect(SOLVERS.map((s) => s.publicKey())).toContain(intent.solver); + }); + + it("only one fill wins when N concurrent fill() calls race on the same accepted intent", async () => { + const intentId = await createIntent(); + await accept(intentId, SEED_SOLVER_KEYPAIRS.ALPHA).expect(201); + + const results = await Promise.all(Array.from({ length: 20 }, () => fill(intentId, SEED_SOLVER_KEYPAIRS.ALPHA))); + + expect(results.filter((r) => r.status === 201)).toHaveLength(1); + const intent = (await http().get(`/api/v1/intents/${intentId}`).expect(200)).body; + expect(intent.state).toBe("filled"); + expect(intent.fillAmount).toBe("995000"); + }); + + it("mixed solvers racing for different intents all resolve with exactly one winner each", async () => { + const intentIds = await Promise.all(Array.from({ length: 3 }, () => createIntent())); + + const results = await Promise.all(intentIds.map((id, i) => accept(id, SOLVERS[i % SOLVERS.length]))); + + expect(results.every((r) => r.status === 201)).toBe(true); + for (const id of intentIds) { + expect((await http().get(`/api/v1/intents/${id}`).expect(200)).body.state).toBe("accepted"); + } + }); + + it("unit-level: acceptIfOpen rejects concurrent calls on the same intent", () => { + const repo = new InMemoryIntentsRepository(); + const [open] = repo.findByState("open"); + + const results = Array.from({ length: 50 }, (_, i) => + repo.acceptIfOpen(open.intentId, `SOLVER_${i}`, Math.floor(Date.now() / 1000) + 300), + ); + + const winners = results.filter((r) => r !== null); + expect(winners).toHaveLength(1); + expect(winners[0]).toMatchObject({ state: "accepted", version: 1 }); + }); + + it("unit-level: fillIfAccepted rejects concurrent calls on the same intent", () => { + const repo = new InMemoryIntentsRepository(); + const [open] = repo.findByState("open"); + + repo.acceptIfOpen(open.intentId, "SOLVER_X", Math.floor(Date.now() / 1000) + 300); + + const results = Array.from({ length: 50 }, () => + repo.fillIfAccepted(open.intentId, "SOLVER_X", { + fillAmount: "995000", + txHash: "race-hash", + filledAt: Math.floor(Date.now() / 1000), + }), + ); + + const winners = results.filter((r) => r !== null); + expect(winners).toHaveLength(1); + expect(winners[0]).toMatchObject({ state: "filled", version: 2 }); + }); + + it("race inventory #473: accept past deadline loses even when it beats the sweeper to the write", async () => { + const repo = new InMemoryIntentsRepository(); + const [open] = repo.findByState("open"); + const pastDeadline = Math.floor(Date.now() / 1000) - 10; + const expired = { ...open, deadline: pastDeadline }; + // Simulate an intent whose deadline elapsed before the accept write lands. + repo.save({ ...expired }); + const now = Math.floor(Date.now() / 1000); + const result = await repo.acceptIfOpen(open.intentId, "SOLVER_LATE", now + 300, now); + expect(result).toBeNull(); + }); + + it("race inventory #473: fill past the accept-extended deadline loses (sweeper slash wins)", async () => { + const repo = new InMemoryIntentsRepository(); + const [open] = repo.findByState("open"); + const now = Math.floor(Date.now() / 1000); + await repo.acceptIfOpen(open.intentId, "SOLVER_X", now + 1, now - 100); + // Advance past the fill window before the fill write lands. + const late = await repo.fillIfAccepted( + open.intentId, + "SOLVER_X", + { fillAmount: "995000", txHash: "late", filledAt: now + 60 }, + now + 60, + ); + expect(late).toBeNull(); + }); + + it("race inventory #473: cancel vs accept — exactly one terminal path wins", async () => { + const repo = new InMemoryIntentsRepository(); + const [open] = repo.findByState("open"); + const now = Math.floor(Date.now() / 1000); + const accepted = await repo.acceptIfOpen(open.intentId, "SOLVER_RACE", now + 300, now); + const cancelled = await repo.cancelIfOpen(open.intentId); + // Exactly one of the two conditional writes may succeed. + expect(Number(accepted !== null) + Number(cancelled !== null)).toBeLessThanOrEqual(1); + }); + + // ── Issue #405: optimistic concurrency, ETag / If-Match, zero lost updates ── + + describe("optimistic concurrency (issue #405)", () => { + it("exposes the version as an ETag and advances it on every mutation", async () => { + const intentId = await createIntent(); + + const read = await http().get(`/api/v1/intents/${intentId}`).expect(200); + expect(read.headers.etag).toBe('"0"'); + expect(read.body.version).toBe(0); + + const accepted = await accept(intentId, SEED_SOLVER_KEYPAIRS.ALPHA).set("If-Match", '"0"').expect(201); + expect(accepted.headers.etag).toBe('"1"'); + + const filled = await fill(intentId, SEED_SOLVER_KEYPAIRS.ALPHA).set("If-Match", '"1"').expect(201); + expect(filled.headers.etag).toBe('"2"'); + }); + + it("rejects a stale If-Match with 412 and leaves the intent untouched", async () => { + const intentId = await createIntent(); + await http().post(`/api/v1/intents/${intentId}/requote`).expect(201); // version 0 → 1 + + const res = await cancel(intentId).set("If-Match", '"0"').expect(412); + expect(res.body).toMatchObject({ currentVersion: 1, currentETag: '"1"' }); + expect((await http().get(`/api/v1/intents/${intentId}`)).body.state).toBe("open"); + }); + + it("rejects a malformed If-Match with 400", async () => { + const intentId = await createIntent(); + await cancel(intentId).set("If-Match", "not-an-etag").expect(400); + }); + + it("lets exactly one of N clients holding the same ETag win; the rest get 412 or 409", async () => { + const intentId = await createIntent(); + const { etag } = (await http().get(`/api/v1/intents/${intentId}`)).headers; + + const results = await Promise.all( + Array.from({ length: 20 }, (_, i) => accept(intentId, SOLVERS[i % SOLVERS.length]).set("If-Match", etag)), + ); + + expect(results.filter((r) => r.status === 201)).toHaveLength(1); + expect(results.filter((r) => r.status !== 201).every((r) => [409, 412].includes(r.status))).toBe(true); + }); + + it("loses zero updates when N writers do read-modify-write with bounded retries", async () => { + const intents = app.get(IntentsService); + const intentId = await createIntent(); + const writers = 25; + + const results = await Promise.all( + Array.from({ length: writers }, () => + intents.mutateWithRetry( + intentId, + (current) => + intents.update( + intentId, + { quotedDstAmount: String(Number(current.quotedDstAmount ?? "0") + 1) }, + current.version, + ), + writers, // enough budget for every writer to eventually win + ), + ), + ); + + expect(results.some(isVersionConflict)).toBe(false); + const final = (await http().get(`/api/v1/intents/${intentId}`).expect(200)).body; + expect(final.quotedDstAmount).toBe(String(writers)); + expect(final.version).toBe(writers); + }); + + it("a late sweeper never slashes fills that landed after it read the intents", async () => { + const intents = app.get(IntentsService); + const sweeper = app.get(IntentsSweeperService); + const registry = app.get(SolverRegistryService); + const slashSpy = jest.spyOn(registry, "slashSolver"); + + const ids = await Promise.all(Array.from({ length: 10 }, () => createIntent())); + await Promise.all(ids.map((id) => accept(id, SEED_SOLVER_KEYPAIRS.ALPHA).expect(201))); + + // The sweeper's snapshot: every intent accepted and (as far as it knows) + // overdue. Then every fill lands before its writes do. + const past = Math.floor(Date.now() / 1000) - 1; + const snapshot = (await intents.getMany(ids)).map((i) => ({ ...i, deadline: past })); + await Promise.all(ids.map((id) => fill(id, SEED_SOLVER_KEYPAIRS.ALPHA).expect(201))); + + const realGetByState = intents.getByState.bind(intents); + const getByState = jest + .spyOn(intents, "getByState") + .mockImplementation(async (state) => (state === "accepted" ? snapshot : realGetByState(state))); + + const result = await sweeper.sweep(); + getByState.mockRestore(); + + expect(result.slashedCount).toBe(0); + expect(slashSpy).not.toHaveBeenCalledWith(expect.objectContaining({ intentId: expect.stringMatching(ids.join("|")) })); + for (const intent of await intents.getMany(ids)) { + expect(intent.state).toBe("filled"); + } + }); + }); +}); diff --git a/test/load/concurrent-idempotent-create.test.ts b/test/load/concurrent-idempotent-create.test.ts index e69de29b..ece354f3 100644 --- a/test/load/concurrent-idempotent-create.test.ts +++ b/test/load/concurrent-idempotent-create.test.ts @@ -0,0 +1,88 @@ +import { INestApplication } from "@nestjs/common"; +import request from "supertest"; +import { randomUUID } from "node:crypto"; +import { createTestApp } from "../utils/create-test-app"; + +/** + * Issue #274 — concurrent-retry load test for the idempotency-key path in + * IntentsService.create(). + * + * Mirrors test/load/concurrent-accept.test.ts: fire N simultaneous POST + * /api/v1/intents requests that all carry the *same* idempotencyKey and assert + * that exactly one intent is created and every response points at it. + */ + +const validCreateBody = { + user: "GRACETESTUSER1234567", + srcChain: "ethereum", + srcTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + srcTokenSymbol: "USDC", + srcTokenDecimals: 6, + srcAmount: "1000000", + dstTokenContract: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA", + dstTokenSymbol: "USDC", + dstTokenDecimals: 7, + minDstAmount: "990000", +}; + +describe("Concurrent idempotent create race load test", () => { + let app: INestApplication; + let baseUrl: string; + + beforeAll(async () => { + app = await createTestApp(); + // Bind once: an unbound server makes supertest open a listener per + // request, which resets connections under concurrency. + await app.listen(0, "127.0.0.1"); + baseUrl = (await app.getUrl()).replace("[::1]", "127.0.0.1"); + }); + + afterAll(async () => { + await app.close(); + }); + + it("creates exactly one intent when N concurrent create() calls share an idempotencyKey", async () => { + const idempotencyKey = randomUUID(); + const concurrency = 25; + + const results = await Promise.allSettled( + Array.from({ length: concurrency }, () => + request(baseUrl) + .post("/api/v1/intents") + .send({ ...validCreateBody, idempotencyKey }), + ), + ); + + const fulfilled = results.filter( + (r): r is PromiseFulfilledResult => r.status === "fulfilled", + ); + const created = fulfilled.filter((r) => r.value.status === 201); + + // Every accepted response must describe the same single intent. + const intentIds = new Set(created.map((r) => r.value.body.intentId)); + expect(intentIds.size).toBe(1); + + const [intentId] = [...intentIds]; + const listed = ( + await request(baseUrl).get("/api/v1/intents").expect(200) + ).body.intents as Array<{ intentId: string }>; + const matches = listed.filter((i) => i.intentId === intentId); + expect(matches).toHaveLength(1); + }); + + it("still creates distinct intents for concurrent calls with different keys", async () => { + const concurrency = 10; + + const results = await Promise.all( + Array.from({ length: concurrency }, () => + request(baseUrl) + .post("/api/v1/intents") + .send({ ...validCreateBody, idempotencyKey: randomUUID() }) + .expect(201), + ), + ); + + const intentIds = new Set(results.map((r) => r.body.intentId)); + expect(intentIds.size).toBe(concurrency); + }); +}); diff --git a/test/load/ws-broadcast-fanout.test.ts b/test/load/ws-broadcast-fanout.test.ts index e69de29b..6096d70e 100644 --- a/test/load/ws-broadcast-fanout.test.ts +++ b/test/load/ws-broadcast-fanout.test.ts @@ -0,0 +1,281 @@ +/** + * Load test: WebSocket broadcast fan-out (#84) + * + * Spins up the real NestJS app on a random port, connects N simultaneous WS + * clients, fires a broadcast, and measures: + * - per-client message delivery latency + * - total fan-out wall-clock time + * - memory growth (heapUsed) before vs after + * + * Run with: + * npx jest --config test/jest-load.json + * or directly: + * npx ts-jest test/load/ws-broadcast-fanout.test.ts + * + * The test does NOT rely on external infrastructure — the server is spun up + * in-process using the standard createTestApp() helper. + */ + +import { INestApplication } from "@nestjs/common"; +import { AddressInfo } from "net"; +import WebSocket from "ws"; +import { createTestApp } from "../utils/create-test-app"; +import { IntentsGateway } from "../../src/intents/intents.gateway"; + +// ── tunables ────────────────────────────────────────────────────────────────── + +/** Subscriber counts to exercise. Each tier runs as a separate test case. */ +const SUBSCRIBER_TIERS = [10, 100, 500]; + +/** + * Hard latency budget per-client (ms). A client is considered "slow" if it + * receives the broadcast more than this many ms after the call to broadcast(). + * Keep generous so the test is not flaky in CI. + */ +const LATENCY_BUDGET_MS = 500; + +/** Maximum allowed heap growth (bytes) across the full fan-out. */ +const HEAP_GROWTH_BUDGET_BYTES = 50 * 1024 * 1024; // 50 MB + +// ── helpers ─────────────────────────────────────────────────────────────────── + +function getWsPort(app: INestApplication): number { + const server = app.getHttpServer() as { address(): AddressInfo | null }; + const addr = server.address(); + if (!addr || typeof addr === "string") throw new Error("could not determine WS port"); + return addr.port; +} + +/** + * Open `count` WebSocket clients against the server, wait until every client + * has received its initial `snapshot` message (meaning the handshake is + * complete and the server has added it to the subscriber set), then resolve + * with the array of open sockets. + * + * Clients are opened in batches: firing all `count` connects in a single tick + * overflows the listener's accept backlog on Windows, which answers the + * overflow with a reset (surfaced here as `ECONNREFUSED`). Waiting for each + * batch's snapshot drains the backlog before the next batch is dialled. + */ +async function openClients( + wsUrl: string, + count: number, + batchSize = 50, +): Promise { + const clients: WebSocket[] = []; + + for (let start = 0; start < count; start += batchSize) { + const size = Math.min(batchSize, count - start); + const batch: WebSocket[] = []; + const ready: Promise[] = []; + + for (let i = 0; i < size; i++) { + const index = start + i; + const ws = new WebSocket(wsUrl); + batch.push(ws); + + ready.push( + new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error(`client ${index} handshake timeout`)), + 10_000, + ); + + ws.on("error", (err) => { + clearTimeout(timeout); + reject(err); + }); + + ws.on("message", (raw) => { + const msg = JSON.parse(raw.toString()) as { type: string }; + // The server sends "connected" then "snapshot" on every connection. + // We wait for the snapshot so we know the client is fully subscribed. + if (msg.type === "snapshot") { + clearTimeout(timeout); + resolve(); + } + }); + }), + ); + } + + await Promise.all(ready); + clients.push(...batch); + } + + return clients; +} + +/** + * Broadcast a single event and measure the time (ms) from when broadcast() + * returns until each client receives the message. + * + * Returns an array of per-client latencies in the order they arrived. + */ +async function measureBroadcastLatency( + gateway: IntentsGateway, + clients: WebSocket[], + eventType = "load_test_ping", +): Promise<{ latencies: number[]; wallClockMs: number }> { + const TARGET_SEQ_MARKER = `__load_test_${Date.now()}`; + const received: number[] = []; + + const waitForAll = new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error(`broadcast not received by all clients within budget`)), + LATENCY_BUDGET_MS * 5, + ); + + const handlers: Map void> = new Map(); + + for (const ws of clients) { + const handler = (raw: Buffer) => { + const msg = JSON.parse(raw.toString()) as { type: string; marker?: string }; + if (msg.type === eventType && msg.marker === TARGET_SEQ_MARKER) { + received.push(Date.now()); + ws.off("message", handler); + handlers.delete(ws); + + if (handlers.size === 0) { + clearTimeout(timeout); + resolve(); + } + } + }; + handlers.set(ws, handler); + ws.on("message", handler); + } + }); + + // Kick off the broadcast and record the start time *after* the call returns + // (broadcast() is async — it resolves after subscriber chain lookups complete). + const broadcastStart = Date.now(); + await gateway.broadcast({ type: eventType, marker: TARGET_SEQ_MARKER }); + const broadcastEnd = Date.now(); + const wallClockMs = broadcastEnd - broadcastStart; + + await waitForAll; + + const latencies = received.map((t) => t - broadcastStart); + return { latencies, wallClockMs }; +} + +/** Close all clients and wait for them to finish. */ +async function closeClients(clients: WebSocket[]): Promise { + await Promise.all( + clients.map( + (ws) => + new Promise((resolve) => { + if (ws.readyState === WebSocket.CLOSED) { + resolve(); + return; + } + ws.on("close", () => resolve()); + ws.close(); + }), + ), + ); +} + +// ── suite ───────────────────────────────────────────────────────────────────── + +describe("WS broadcast fan-out load test (#84)", () => { + let app: INestApplication; + let wsUrl: string; + let gateway: IntentsGateway; + let previousMaxPerIp: string | undefined; + + beforeAll(async () => { + // Every client in this suite connects from 127.0.0.1, so the default + // per-IP admission cap (WS_MAX_CONNECTIONS_PER_IP, 20) would reject the + // 100- and 500-subscriber tiers at the handshake — the test would then + // measure the cap instead of fan-out. Raise it above the largest tier for + // this app only; production keeps the default. + previousMaxPerIp = process.env.WS_MAX_CONNECTIONS_PER_IP; + process.env.WS_MAX_CONNECTIONS_PER_IP = String( + Math.max(SUBSCRIBER_TIERS[SUBSCRIBER_TIERS.length - 1] + 100, 1000), + ); + + app = await createTestApp(); + // Listen on a random OS-assigned port to avoid collisions in CI + await app.listen(0); + const port = getWsPort(app); + wsUrl = `ws://127.0.0.1:${port}/ws`; + gateway = app.get(IntentsGateway); + }, 30_000); + + afterAll(async () => { + await app.close(); + if (previousMaxPerIp === undefined) { + delete process.env.WS_MAX_CONNECTIONS_PER_IP; + } else { + process.env.WS_MAX_CONNECTIONS_PER_IP = previousMaxPerIp; + } + }, 15_000); + + for (const subscriberCount of SUBSCRIBER_TIERS) { + // Use a describe block per tier so failures are clearly labelled + describe(`${subscriberCount} concurrent subscribers`, () => { + let clients: WebSocket[] = []; + + beforeAll(async () => { + clients = await openClients(wsUrl, subscriberCount); + }, 30_000); + + afterAll(async () => { + await closeClients(clients); + }, 15_000); + + it(`delivers broadcast to all ${subscriberCount} clients`, async () => { + const { latencies } = await measureBroadcastLatency(gateway, clients); + expect(latencies).toHaveLength(subscriberCount); + }, 30_000); + + it(`all clients receive within ${LATENCY_BUDGET_MS}ms latency budget`, async () => { + const { latencies } = await measureBroadcastLatency(gateway, clients, "latency_check"); + const slowClients = latencies.filter((l) => l > LATENCY_BUDGET_MS); + + // Log a summary regardless of pass/fail so CI logs are informative + const p50 = percentile(latencies, 50); + const p95 = percentile(latencies, 95); + const p99 = percentile(latencies, 99); + console.log( + `[load-test] subscribers=${subscriberCount} ` + + `p50=${p50}ms p95=${p95}ms p99=${p99}ms ` + + `slow=${slowClients.length}`, + ); + + expect(slowClients.length).toBe(0); + }, 30_000); + + it(`synchronous fan-out wall-clock time stays proportional`, async () => { + const heapBefore = process.memoryUsage().heapUsed; + + const { wallClockMs } = await measureBroadcastLatency(gateway, clients, "wall_clock_check"); + + const heapAfter = process.memoryUsage().heapUsed; + const heapGrowth = heapAfter - heapBefore; + + console.log( + `[load-test] subscribers=${subscriberCount} ` + + `wallClock=${wallClockMs}ms heapGrowth=${(heapGrowth / 1024).toFixed(1)}KB`, + ); + + // Wall-clock should be low because broadcast() iterates synchronously + // and ws.send() is non-blocking (it enqueues into libuv). + // Allow a loose upper bound rather than asserting an exact number. + expect(wallClockMs).toBeLessThan(1000); + expect(heapGrowth).toBeLessThan(HEAP_GROWTH_BUDGET_BYTES); + }, 30_000); + }); + } +}); + +// ── statistics helpers ──────────────────────────────────────────────────────── + +function percentile(sortedOrUnsorted: number[], p: number): number { + if (sortedOrUnsorted.length === 0) return 0; + const sorted = [...sortedOrUnsorted].sort((a, b) => a - b); + const idx = Math.ceil((p / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; +} diff --git a/test/params.e2e-spec.ts b/test/params.e2e-spec.ts index e69de29b..ee773f40 100644 --- a/test/params.e2e-spec.ts +++ b/test/params.e2e-spec.ts @@ -0,0 +1,179 @@ +/** + * E2E tests for GET /api/v1/params (issue #500). + * + * Verifies the shape and semantics of the governance parameters endpoint + * against a real booted Nest app (no contract configured → code defaults). + */ + +// Default import: with `esModuleInterop` the namespace import is an object +// wrapper, not the callable `request` function, so `request(server)` throws +// "request is not a function". +import request from "supertest"; +import { INestApplication } from "@nestjs/common"; +import { createTestApp } from "./utils/create-test-app"; + +describe("GET /api/v1/params (e2e)", () => { + let app: INestApplication; + + beforeAll(async () => { + app = await createTestApp(); + }); + + afterAll(async () => { + await app.close(); + }); + + // -------------------------------------------------------------------------- + // Basic response shape + // -------------------------------------------------------------------------- + + it("returns 200 OK", async () => { + await request(app.getHttpServer()).get("/api/v1/params").expect(200); + }); + + it("returns JSON content-type", async () => { + await request(app.getHttpServer()) + .get("/api/v1/params") + .expect("Content-Type", /application\/json/); + }); + + it("response body has current, pending, and history keys", async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params").expect(200); + expect(res.body).toHaveProperty("current"); + expect(res.body).toHaveProperty("pending"); + expect(res.body).toHaveProperty("history"); + }); + + // -------------------------------------------------------------------------- + // `current` object shape and defaults (no PARAMS_CONTRACT_ID configured) + // -------------------------------------------------------------------------- + + describe("current params (code defaults)", () => { + let current: Record; + + beforeAll(async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params").expect(200); + current = res.body.current as Record; + }); + + it("has a numeric version >= 0", () => { + expect(typeof current["version"]).toBe("number"); + expect(current["version"] as number).toBeGreaterThanOrEqual(0); + }); + + it("has a numeric feeBps >= 0", () => { + expect(typeof current["feeBps"]).toBe("number"); + expect(current["feeBps"] as number).toBeGreaterThanOrEqual(0); + }); + + it("has feeBps = 30 (code default)", () => { + expect(current["feeBps"]).toBe(30); + }); + + it("has a chains object with stellar entry", () => { + expect(current["chains"]).toBeDefined(); + expect(typeof current["chains"]).toBe("object"); + const chains = current["chains"] as Record; + expect(chains["stellar"]).toBeDefined(); + }); + + it("stellar chain has deadlineSeconds = 900 (code default)", () => { + const chains = current["chains"] as Record; + expect(chains["stellar"]?.deadlineSeconds).toBe(900); + }); + + it("stellar chain has fillWindowSeconds = 120 (code default)", () => { + const chains = current["chains"] as Record; + expect(chains["stellar"]?.fillWindowSeconds).toBe(120); + }); + + it("has maxExposureRatio as a number between 0 and 1", () => { + expect(typeof current["maxExposureRatio"]).toBe("number"); + expect(current["maxExposureRatio"] as number).toBeGreaterThanOrEqual(0); + expect(current["maxExposureRatio"] as number).toBeLessThanOrEqual(1); + }); + + it("has slashAmount as a numeric string", () => { + expect(typeof current["slashAmount"]).toBe("string"); + expect(() => BigInt(current["slashAmount"] as string)).not.toThrow(); + }); + + it("has activeSinceLedger as a number", () => { + expect(typeof current["activeSinceLedger"]).toBe("number"); + }); + + it("has adoptedAt as an ISO-8601 date string", () => { + expect(typeof current["adoptedAt"]).toBe("string"); + expect(new Date(current["adoptedAt"] as string).toISOString()).toBe(current["adoptedAt"]); + }); + + it("includes all 7 supported chains in the chains object", () => { + const chains = current["chains"] as Record; + const expectedChains = [ + "stellar", + "ethereum", + "base", + "polygon", + "arbitrum", + "optimism", + "avalanche", + ]; + for (const chain of expectedChains) { + expect(chains[chain]).toBeDefined(); + } + }); + }); + + // -------------------------------------------------------------------------- + // `pending` — should be null when no contract is configured + // -------------------------------------------------------------------------- + + it("pending is null when PARAMS_CONTRACT_ID is not set", async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params").expect(200); + expect(res.body.pending).toBeNull(); + }); + + // -------------------------------------------------------------------------- + // `history` — should be empty array on first boot + // -------------------------------------------------------------------------- + + it("history is an array", async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params").expect(200); + expect(Array.isArray(res.body.history)).toBe(true); + }); + + it("history is empty on first boot (no contract, no changes)", async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params").expect(200); + expect(res.body.history).toHaveLength(0); + }); + + // -------------------------------------------------------------------------- + // Security headers + // -------------------------------------------------------------------------- + + it("returns X-Content-Type-Options: nosniff (Helmet)", async () => { + const res = await request(app.getHttpServer()).get("/api/v1/params"); + expect(res.headers["x-content-type-options"]).toBe("nosniff"); + }); + + // -------------------------------------------------------------------------- + // Idempotency — two calls return consistent data + // -------------------------------------------------------------------------- + + it("returns identical current.version on successive calls", async () => { + const [r1, r2] = await Promise.all([ + request(app.getHttpServer()).get("/api/v1/params"), + request(app.getHttpServer()).get("/api/v1/params"), + ]); + expect(r1.body.current.version).toBe(r2.body.current.version); + expect(r1.body.current.feeBps).toBe(r2.body.current.feeBps); + }); + + // -------------------------------------------------------------------------- + // Swagger / OpenAPI registration + // -------------------------------------------------------------------------- + + it("GET /docs returns 200 (Swagger UI includes the params route)", async () => { + await request(app.getHttpServer()).get("/docs").expect(200); + }); +}); From 16e86d0c625d922923afcb7079e6d2e6cafd28b3 Mon Sep 17 00:00:00 2001 From: benedictworks-home Date: Wed, 30 Sep 2026 10:12:20 -0700 Subject: [PATCH 2/3] fix: repair restored files damaged by interleaved merges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same merge run that emptied 55 files left several of their last-non-empty versions with interleaved content, so the restore in the previous commit brought back files that do not compile. This repairs every damaged site found: - intents.gateway.ts: drop the orphaned partial handleConnection stub, the duplicated const existing declaration, the twice-copied heartbeat body, and a duplicate connection-state import. - configuration.ts / solver-registry.service.spec.ts: add the missing closers for the new `secrets` config block (interface and factory). - stellar-signature.ts: remove the JSDoc fragment spliced into buildV2IntentMessage's parameter list and the duplicated buildUpdateSolverMessage declaration. - soroban.service.ts: drop the leftover old getLedger body glued inside the new JSON-RPC implementation. - Drop duplicate imports in fill-intent.dto, governance.module, tokens.service, solvers.controller, shadow.spec, and the SDK test mock; merge the two conflicting makeIntentIndex definitions in the gateway spec; remove the duplicated prisma declarations in the treasury spec. - package.json + package-lock.json: revert to 77bf137e, the last pair that parses and satisfies each other — every later lock revision in history is unparseable JSON (spliced mid-string), so npm ci cannot work from them. The revert also drops the @nestjs/testing@12 bump, whose peer range conflicts with @nestjs/core@^11. Verified with Node's module.stripTypeScriptTypes (all 50 changed .ts files parse) plus a duplicate import/declaration scan and JSON.parse on the lockfile. Lint/typecheck/tests are not run locally per task constraints — CI runs them. --- package-lock.json | 3216 ++++++------------- package.json | 12 +- src/common/stellar-signature.ts | 15 - src/config/configuration.ts | 2 + src/governance/governance.module.ts | 1 - src/intents/dto/fill-intent.dto.ts | 2 - src/intents/intents.gateway.spec.ts | 12 +- src/intents/intents.gateway.ts | 28 - src/intents/intents.service.shadow.spec.ts | 1 - src/solvers/solvers.controller.ts | 4 - src/soroban/signer.service.spec.ts | 28 - src/soroban/solver-registry.service.spec.ts | 1 + src/soroban/soroban.service.ts | 7 - src/tokens/tokens.service.ts | 1 - src/treasury/treasury.service.spec.ts | 13 - src/treasury/treasury.service.ts | 4 - test/__mocks__/@stellar/stellar-sdk.ts | 1 - 17 files changed, 976 insertions(+), 2372 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9be432ef..c37b9be7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "dotenv": "^16.4.5", "helmet": "^7.1.0", "ioredis": "^6.0.0", - "joi": "^18.2.9", + "joi": "^18.2.3", "parquetjs": "^0.11.2", "pg": "8.23.0", "prom-client": "^15.1.3", @@ -52,15 +52,15 @@ "viem": "^2.44.4", "winston": "^3.13.0", "ws": "^8.18.0", - "zod": "^4.6.5" + "zod": "^3.23.8" }, "devDependencies": { - "@commitlint/cli": "^21.2.3", - "@commitlint/config-conventional": "^21.2.3", + "@commitlint/cli": "^19.8.1", + "@commitlint/config-conventional": "^19.8.1", "@msgpack/msgpack": "^3.0.0", "@nestjs/cli": "^11.0.24", "@nestjs/schematics": "^11.1.0", - "@nestjs/testing": "^12.1.0", + "@nestjs/testing": "^11.1.28", "@stryker-mutator/core": "^8.0.0", "@stryker-mutator/jest-runner": "^8.0.0", "@types/cors": "^2.8.17", @@ -71,7 +71,7 @@ "@types/supertest": "^7.2.0", "@types/uuid": "^10.0.0", "@types/ws": "^8.5.12", - "@typescript-eslint/eslint-plugin": "^8.70.1", + "@typescript-eslint/eslint-plugin": "^7.13.0", "@typescript-eslint/parser": "^7.13.0", "eslint": "^8.57.0", "fast-check": "^4.10.2", @@ -1053,770 +1053,479 @@ } }, "node_modules/@commitlint/cli": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.3.tgz", - "integrity": "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz", + "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-conventional": "^21.2.3", - "@commitlint/format": "^21.2.3", - "@commitlint/lint": "^21.2.3", - "@commitlint/load": "^21.2.3", - "@commitlint/read": "^21.2.3", - "@commitlint/types": "^21.2.3", + "@commitlint/format": "^19.8.1", + "@commitlint/lint": "^19.8.1", + "@commitlint/load": "^19.8.1", + "@commitlint/read": "^19.8.1", + "@commitlint/types": "^19.8.1", "tinyexec": "^1.0.0", - "yargs": "^18.0.0" + "yargs": "^17.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/@commitlint/config-conventional": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", - "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "node_modules/@commitlint/config-conventional": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", + "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-conventionalcommits": "^10.0.0" + "@commitlint/types": "^19.8.1", + "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/config-validator": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", + "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" + "@commitlint/types": "^19.8.1", + "ajv": "^8.11.0" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "node_modules/@commitlint/ensure": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz", + "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@commitlint/types": "^19.8.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/@commitlint/execute-rule": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", + "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "node_modules/@commitlint/format": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz", + "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0" }, "engines": { - "node": ">=20" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/cliui/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@commitlint/cli/node_modules/conventional-changelog-conventionalcommits": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", - "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "node_modules/@commitlint/is-ignored": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz", + "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@conventional-changelog/template": "^1.4.0" + "@commitlint/types": "^19.8.1", + "semver": "^7.6.0" }, "engines": { - "node": ">=22" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@commitlint/cli/node_modules/string-width": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", - "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "node_modules/@commitlint/lint": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz", + "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==", "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" + "@commitlint/is-ignored": "^19.8.1", + "@commitlint/parse": "^19.8.1", + "@commitlint/rules": "^19.8.1", + "@commitlint/types": "^19.8.1" }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=v18" } }, - "node_modules/@commitlint/cli/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "node_modules/@commitlint/load": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", + "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.2.2" + "@commitlint/config-validator": "^19.8.1", + "@commitlint/execute-rule": "^19.8.1", + "@commitlint/resolve-extends": "^19.8.1", + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" }, "engines": { - "node": ">=12" + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@commitlint/cli/node_modules/wrap-ansi": { + "node_modules/@commitlint/load/node_modules/cosmiconfig": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": ">=18" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@commitlint/cli/node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "jiti": "2.6.1" }, "engines": { - "node": ">=18" + "node": ">=v18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" } }, - "node_modules/@commitlint/cli/node_modules/yargs": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", - "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "node_modules/@commitlint/load/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" + } + ], "license": "MIT", "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^8.2.1", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" + "argparse": "^2.0.1" }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@commitlint/cli/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "node_modules/@commitlint/message": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz", + "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" + "node": ">=v18" } }, - "node_modules/@commitlint/config-conventional": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", - "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "node_modules/@commitlint/parse": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz", + "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-conventionalcommits": "^10.0.0" + "@commitlint/types": "^19.8.1", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/config-conventional/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/read": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz", + "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" + "@commitlint/top-level": "^19.8.1", + "@commitlint/types": "^19.8.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/config-conventional/node_modules/conventional-commits-parser": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", - "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "node_modules/@commitlint/resolve-extends": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", + "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^2.0.0", - "argue-cli": "^3.1.0" - }, - "bin": { - "conventional-commits-parser": "dist/cli/index.js" + "@commitlint/config-validator": "^19.8.1", + "@commitlint/types": "^19.8.1", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=22" + "node": ">=v18" } }, - "node_modules/@commitlint/config-validator": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.3.tgz", - "integrity": "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw==", + "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "@commitlint/types": "^21.2.3", - "ajv": "^8.11.0" - }, "engines": { - "node": ">=22.12.0" + "node": ">=8" } }, - "node_modules/@commitlint/config-validator/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/rules": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz", + "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" + "@commitlint/ensure": "^19.8.1", + "@commitlint/message": "^19.8.1", + "@commitlint/to-lines": "^19.8.1", + "@commitlint/types": "^19.8.1" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/ensure": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.3.tgz", - "integrity": "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA==", + "node_modules/@commitlint/to-lines": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz", + "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==", "dev": true, "license": "MIT", - "dependencies": { - "@commitlint/types": "^21.2.3", - "es-toolkit": "^1.46.0" - }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/ensure/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/top-level": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz", + "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" + "find-up": "^7.0.0" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/execute-rule": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", - "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, "engines": { - "node": ">=22.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/format": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.3.tgz", - "integrity": "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ==", + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.2.3", - "picocolors": "^1.1.1" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=22.12.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/format/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=22.12.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/is-ignored": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.3.tgz", - "integrity": "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w==", + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.2.3", - "semver": "^7.6.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=22.12.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/is-ignored/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, "engines": { - "node": ">=22.12.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@commitlint/lint": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.3.tgz", - "integrity": "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg==", + "node_modules/@commitlint/top-level/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, "license": "MIT", - "dependencies": { - "@commitlint/is-ignored": "^21.2.3", - "@commitlint/parse": "^21.2.3", - "@commitlint/rules": "^21.2.3", - "@commitlint/types": "^21.2.3" - }, "engines": { - "node": ">=22.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/lint/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "dev": true, "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, "engines": { - "node": ">=22.12.0" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/load": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.3.tgz", - "integrity": "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw==", + "node_modules/@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^21.2.3", - "@commitlint/execute-rule": "^21.0.1", - "@commitlint/resolve-extends": "^21.2.3", - "@commitlint/types": "^21.2.3", - "cosmiconfig": "^9.0.1", - "cosmiconfig-typescript-loader": "^6.1.0", - "es-toolkit": "^1.46.0", - "is-plain-obj": "^4.1.0", - "picocolors": "^1.1.1" + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" }, "engines": { - "node": ">=22.12.0" + "node": ">=v18" } }, - "node_modules/@commitlint/load/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/load/node_modules/cosmiconfig": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", - "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", - "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jiti": "2.6.1" - }, - "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" - } - }, - "node_modules/@commitlint/load/node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@commitlint/message": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", - "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/parse": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.3.tgz", - "integrity": "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-angular": "^9.0.0", - "conventional-commits-parser": "^7.0.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/parse/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/read": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.3.tgz", - "integrity": "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/top-level": "^21.2.0", - "@commitlint/types": "^21.2.3", - "@conventional-changelog/git-client": "^3.0.0", - "tinyexec": "^1.0.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/read/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/resolve-extends": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.3.tgz", - "integrity": "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^21.2.3", - "@commitlint/types": "^21.2.3", - "es-toolkit": "^1.46.0", - "global-directory": "^5.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/resolve-extends/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@commitlint/rules": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.3.tgz", - "integrity": "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/ensure": "^21.2.3", - "@commitlint/message": "^21.2.0", - "@commitlint/to-lines": "^21.0.1", - "@commitlint/types": "^21.2.3" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/rules/node_modules/@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/to-lines": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", - "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/top-level": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", - "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@commitlint/types": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", - "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/conventional-commits-parser": "^5.0.0", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@conventional-changelog/git-client": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", - "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/child-process-utils": "^2.0.0", - "@simple-libs/stream-utils": "^2.0.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=22" - }, - "peerDependencies": { - "conventional-commits-filter": "^6.0.1", - "conventional-commits-parser": "^7.1.2" - }, - "peerDependenciesMeta": { - "conventional-commits-filter": { - "optional": true - }, - "conventional-commits-parser": { - "optional": true - } - } - }, - "node_modules/@conventional-changelog/template": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", - "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.8", - "resolved": "https://regis - "node_modules/@conventional-changelog/template": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", - "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@dabh/diagnostics": { @@ -3972,11 +3681,10 @@ } }, "node_modules/@nestjs/testing": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-12.1.0.tgz", - "integrity": "sha512-OL6ar+p4BRy26YlOk6nqSchGShQajW0jaPSz2tvP7C3dLQcb26Wm0AG2IlvqIBoHuN3zQFMc5DoLAyiosPP4pw==", + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.28.tgz", + "integrity": "sha512-B+VgRxeLaH7jkOMgAyUP3N3rpFlisQ7JRxixRbgHvG6a0VgKbbkNSofKExexCgKmQQak80undb3+2kE1lUBmRQ==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "2.8.1" }, @@ -3985,10 +3693,10 @@ "url": "https://opencollective.com/nest" }, "peerDependencies": { - "@nestjs/common": "^12.0.0", - "@nestjs/core": "^12.0.0", - "@nestjs/microservices": "^12.0.0", - "@nestjs/platform-express": "^12.0.0" + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0" }, "peerDependenciesMeta": { "@nestjs/microservices": { @@ -5950,49 +5658,6 @@ "node": ">=18" } }, - "node_modules/@simple-libs/child-process-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", - "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/stream-utils": "^2.0.0" - }, - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, - "node_modules/@simple-libs/stream-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", - "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, - "node_modules/@sinclair/typebox" - "node_modules/@simple-libs/stream-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", - "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, "node_modules/@sinclair/typebox": { "version": "0.34.52", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", @@ -7166,118 +6831,36 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", - "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/type-utils": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "ignore": "^7.0.5", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.70.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", - "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "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" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/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, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", - "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/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, - "license": "MIT", - "engines": { - "node": ">=18.12" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, - "peerDependencies": { - "typescript": ">=4.8.4" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/parser": { @@ -7308,42 +6891,6 @@ } } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", - "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.70.1", - "@typescript-eslint/types": "^8.70.1", - "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" - } - }, - "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", @@ -7361,171 +6908,31 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", - "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", - "dev": true, - "license": "MIT", - "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" - } - }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", - "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "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" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", - "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.70.1", - "@typescript-eslint/tsconfig-utils": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "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" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@typescript-eslint/type-utils/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, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/type-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, - "license": "MIT", - "engines": { - "node": ">=18.12" + "eslint": "^8.56.0" }, - "peerDependencies": { - "typescript": ">=4.8.4" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/types": { @@ -7570,170 +6977,25 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", - "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1" - }, - "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" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", - "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", - "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.70.1", - "@typescript-eslint/tsconfig-utils": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "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": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "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" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@typescript-eslint/utils/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, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.8" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/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, - "license": "MIT", - "engines": { - "node": ">=18.12" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { @@ -8449,19 +7711,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/argue-cli": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", - "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, "node_modules/array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -9432,46 +8681,48 @@ } }, "node_modules/conventional-changelog-angular": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", - "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "license": "ISC", "dependencies": { - "@conventional-changelog/template": "^1.4.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=22" + "node": ">=16" } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", - "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "license": "ISC", "dependencies": { - "@conventional-changelog/template": "^1.4.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=22" + "node": ">=16" } }, "node_modules/conventional-commits-parser": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", - "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^2.0.0", - "argue-cli": "^3.1.0" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" }, "bin": { - "conventional-commits-parser": "dist/cli/index.js" + "conventional-commits-parser": "cli.mjs" }, "engines": { - "node": ">=22" + "node": ">=16" } }, "node_modules/convert-source-map": { @@ -9608,6 +8859,19 @@ "node": ">= 8" } }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -9973,19 +9237,6 @@ "node": ">= 0.4" } }, - "node_modules/es-toolkit": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", - "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", - "dev": true, - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks", - "tests/types", - "tests/browser-compat" - ] - }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -10572,24 +9823,6 @@ "bser": "2.1.1" } }, - "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, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -11117,19 +10350,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", - "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -11186,6 +10406,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "deprecated": "Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -11258,16 +10497,16 @@ } }, "node_modules/global-directory": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", - "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "license": "MIT", "dependencies": { - "ini": "6.0.0" + "ini": "4.1.1" }, "engines": { - "node": ">=20" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11588,214 +10827,63 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/index-to-position": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", - "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "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.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "ISC", "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "node": ">=0.8.19" } }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/int53": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/int53/-/int53-0.2.4.tgz", - "integrity": "sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g==", - "license": "BSD-3-Clause" - }, - "node_modules/ioredis": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", - "integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==", - "license": "MIT", + "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==", + "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.", + "dev": true, "dependencies": { - "@ioredis/commands": "2.0.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "standard-as-callback": "2.1.0" - }, + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/int53": { @@ -11954,6 +11042,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", @@ -12743,10 +11844,9 @@ } }, "node_modules/joi": { - "version": "18.2.9", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", - "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", - "license": "BSD-3-Clause", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz", + "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==", "dependencies": { "@hapi/address": "^5.1.1", "@hapi/formula": "^3.0.2", @@ -12879,6 +11979,33 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -12990,6 +12117,20 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -13002,6 +12143,41 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -13148,6 +12324,19 @@ "node": ">= 4.0.0" } }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -14011,8 +13200,8 @@ }, "node_modules/pg-connection-string": { "version": "2.14.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", - "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", "license": "MIT" }, "node_modules/pg-int8": { @@ -15058,6 +14247,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, "license": "ISC", "engines": { "node": ">= 10.x" @@ -15587,6 +14777,19 @@ "node": "*" } }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -15620,47 +14823,15 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity" - }, - "node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, - "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, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -16756,10 +15927,9 @@ } }, "node_modules/zod": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", - "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", - "license": "MIT", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -17432,367 +16602,157 @@ }, "@bull-board/api": { "version": "9.10.1", - "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-9.10.1.tgz", - "integrity": "sha512-yaFVInFvQAKHQwW7xxdoWQaC2xW1f8JOAZ5k6Zl8GQBTnlhm6UmeRQUr2Gb6wMgfl5v61p2eSupPow2CEvz0ng==", - "requires": { - "redis-info": "^3.1.0", - "valibot": "^1.5.0" - } - }, - "@bull-board/express": { - "version": "9.10.1", - "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-9.10.1.tgz", - "integrity": "sha512-sUXrFAwbllDNGyieB51Xr7bFa+f7uPdWWPMcArROYlAXsZqrG0HyRPf81bpCX8ZnBGTUcbsEq9jSdRpEfbqm7g==", - "requires": { - "@bull-board/api": "9.10.1", - "@bull-board/ui": "9.10.1", - "ejs": "^6.0.1", - "express": "^5.2.1" - } - }, - "@bull-board/ui": { - "version": "9.10.1", - "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-9.10.1.tgz", - "integrity": "sha512-1E0zbLaljf+Zhx3nDUotzsG2Dz7/4i72nWCxKkFwxfz65MKXfObo2mXWMuIDoXAeWSa3sm7hlPj3h3scts4DaQ==", - "requires": { - "@bull-board/api": "9.10.1" - } - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true - }, - "@commitlint/cli": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.3.tgz", - "integrity": "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ==", - "dev": true, - "requires": { - "@commitlint/config-conventional": "^21.2.3", - "@commitlint/format": "^21.2.3", - "@commitlint/lint": "^21.2.3", - "@commitlint/load": "^21.2.3", - "@commitlint/read": "^21.2.3", - "@commitlint/types": "^21.2.3", - "tinyexec": "^1.0.0", - "yargs": "^18.0.0" - }, - "dependencies": { - "@commitlint/config-conventional": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", - "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", - "dev": true, - "requires": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-conventionalcommits": "^10.0.0" - } - }, - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - }, - "ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true - }, - "cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "requires": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "dependencies": { - "string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "requires": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - } - } - } - }, - "conventional-changelog-conventionalcommits": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", - "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", - "dev": true, - "requires": { - "@conventional-changelog/template": "^1.4.0" - } - }, - "emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true - }, - "string-width": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", - "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", - "dev": true, - "requires": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - } - }, - "strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "requires": { - "ansi-regex": "^6.2.2" - } - }, - "wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "requires": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "dependencies": { - "string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "requires": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - } - } - } - }, - "yargs": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", - "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", - "dev": true, - "requires": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^8.2.1", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - } - }, - "yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true - } + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-9.10.1.tgz", + "integrity": "sha512-yaFVInFvQAKHQwW7xxdoWQaC2xW1f8JOAZ5k6Zl8GQBTnlhm6UmeRQUr2Gb6wMgfl5v61p2eSupPow2CEvz0ng==", + "requires": { + "redis-info": "^3.1.0", + "valibot": "^1.5.0" + } + }, + "@bull-board/express": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-9.10.1.tgz", + "integrity": "sha512-sUXrFAwbllDNGyieB51Xr7bFa+f7uPdWWPMcArROYlAXsZqrG0HyRPf81bpCX8ZnBGTUcbsEq9jSdRpEfbqm7g==", + "requires": { + "@bull-board/api": "9.10.1", + "@bull-board/ui": "9.10.1", + "ejs": "^6.0.1", + "express": "^5.2.1" + } + }, + "@bull-board/ui": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-9.10.1.tgz", + "integrity": "sha512-1E0zbLaljf+Zhx3nDUotzsG2Dz7/4i72nWCxKkFwxfz65MKXfObo2mXWMuIDoXAeWSa3sm7hlPj3h3scts4DaQ==", + "requires": { + "@bull-board/api": "9.10.1" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, + "@commitlint/cli": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz", + "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==", + "dev": true, + "requires": { + "@commitlint/format": "^19.8.1", + "@commitlint/lint": "^19.8.1", + "@commitlint/load": "^19.8.1", + "@commitlint/read": "^19.8.1", + "@commitlint/types": "^19.8.1", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" } }, "@commitlint/config-conventional": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", - "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", + "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-conventionalcommits": "^10.0.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - }, - "conventional-commits-parser": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", - "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", - "dev": true, - "requires": { - "@simple-libs/stream-utils": "^2.0.0", - "argue-cli": "^3.1.0" - } - } + "@commitlint/types": "^19.8.1", + "conventional-changelog-conventionalcommits": "^7.0.2" } }, "@commitlint/config-validator": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.3.tgz", - "integrity": "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", + "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", + "@commitlint/types": "^19.8.1", "ajv": "^8.11.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } } }, "@commitlint/ensure": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.3.tgz", - "integrity": "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz", + "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", - "es-toolkit": "^1.46.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } + "@commitlint/types": "^19.8.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" } }, "@commitlint/execute-rule": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", - "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", + "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", "dev": true }, "@commitlint/format": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.3.tgz", - "integrity": "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz", + "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", - "picocolors": "^1.1.1" + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0" }, "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } + "chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true } } }, "@commitlint/is-ignored": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.3.tgz", - "integrity": "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz", + "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", + "@commitlint/types": "^19.8.1", "semver": "^7.6.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } } }, "@commitlint/lint": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.3.tgz", - "integrity": "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz", + "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==", "dev": true, "requires": { - "@commitlint/is-ignored": "^21.2.3", - "@commitlint/parse": "^21.2.3", - "@commitlint/rules": "^21.2.3", - "@commitlint/types": "^21.2.3" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } + "@commitlint/is-ignored": "^19.8.1", + "@commitlint/parse": "^19.8.1", + "@commitlint/rules": "^19.8.1", + "@commitlint/types": "^19.8.1" } }, "@commitlint/load": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.3.tgz", - "integrity": "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", + "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", "dev": true, "requires": { - "@commitlint/config-validator": "^21.2.3", - "@commitlint/execute-rule": "^21.0.1", - "@commitlint/resolve-extends": "^21.2.3", - "@commitlint/types": "^21.2.3", - "cosmiconfig": "^9.0.1", + "@commitlint/config-validator": "^19.8.1", + "@commitlint/execute-rule": "^19.8.1", + "@commitlint/resolve-extends": "^19.8.1", + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", "cosmiconfig-typescript-loader": "^6.1.0", - "es-toolkit": "^1.46.0", - "is-plain-obj": "^4.1.0", - "picocolors": "^1.1.1" + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" }, "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } + "chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true }, "cosmiconfig": { "version": "9.0.2", @@ -17816,9 +16776,9 @@ } }, "js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "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" @@ -17827,81 +16787,49 @@ } }, "@commitlint/message": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", - "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz", + "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==", "dev": true }, "@commitlint/parse": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.3.tgz", - "integrity": "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz", + "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==", "dev": true, "requires": { - "@commitlint/types": "^21.2.3", - "conventional-changelog-angular": "^9.0.0", - "conventional-commits-parser": "^7.0.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } + "@commitlint/types": "^19.8.1", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" } }, "@commitlint/read": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.3.tgz", - "integrity": "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz", + "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==", "dev": true, "requires": { - "@commitlint/top-level": "^21.2.0", - "@commitlint/types": "^21.2.3", - "@conventional-changelog/git-client": "^3.0.0", + "@commitlint/top-level": "^19.8.1", + "@commitlint/types": "^19.8.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", "tinyexec": "^1.0.0" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } } }, "@commitlint/resolve-extends": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.3.tgz", - "integrity": "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", + "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", "dev": true, "requires": { - "@commitlint/config-validator": "^21.2.3", - "@commitlint/types": "^21.2.3", - "es-toolkit": "^1.46.0", - "global-directory": "^5.0.0", + "@commitlint/config-validator": "^19.8.1", + "@commitlint/types": "^19.8.1", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" }, "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -17911,42 +16839,88 @@ } }, "@commitlint/rules": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.3.tgz", - "integrity": "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz", + "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==", "dev": true, "requires": { - "@commitlint/ensure": "^21.2.3", - "@commitlint/message": "^21.2.0", - "@commitlint/to-lines": "^21.0.1", - "@commitlint/types": "^21.2.3" - }, - "dependencies": { - "@commitlint/types": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", - "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", - "dev": true, - "requires": { - "conventional-commits-parser": "^7.0.0", - "picocolors": "^1.1.1" - } - } + "@commitlint/ensure": "^19.8.1", + "@commitlint/message": "^19.8.1", + "@commitlint/to-lines": "^19.8.1", + "@commitlint/types": "^19.8.1" } }, "@commitlint/to-lines": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", - "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz", + "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==", "dev": true }, "@commitlint/top-level": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", - "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz", + "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==", "dev": true, "requires": { - "escalade": "^3.2.0" + "find-up": "^7.0.0" + }, + "dependencies": { + "find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "requires": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + } + }, + "locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "requires": { + "p-locate": "^6.0.0" + } + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true + }, + "unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true + }, + "yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true + } } }, "@commitlint/types": { @@ -17967,23 +16941,6 @@ } } }, - "@conventional-changelog/git-client": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", - "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", - "dev": true, - "requires": { - "@simple-libs/child-process-utils": "^2.0.0", - "@simple-libs/stream-utils": "^2.0.0", - "semver": "^7.5.2" - } - }, - "@conventional-changelog/template": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", - "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", - "dev": true - }, "@dabh/diagnostics": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", @@ -19300,9 +18257,9 @@ } }, "@nestjs/testing": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-12.1.0.tgz", - "integrity": "sha512-OL6ar+p4BRy26YlOk6nqSchGShQajW0jaPSz2tvP7C3dLQcb26Wm0AG2IlvqIBoHuN3zQFMc5DoLAyiosPP4pw==", + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.28.tgz", + "integrity": "sha512-B+VgRxeLaH7jkOMgAyUP3N3rpFlisQ7JRxixRbgHvG6a0VgKbbkNSofKExexCgKmQQak80undb3+2kE1lUBmRQ==", "dev": true, "requires": { "tslib": "2.8.1" @@ -20512,31 +19469,6 @@ "meriyah": "^6.1.4" } }, - "@simple-libs/child-process-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", - "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", - "dev": true, - "requires": { - "@simple-libs/stream-utils": "^2.0.0" - } - }, - "@simple-libs/stream-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", - "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", - "dev": true - }, - "@sinclair/typebox": { - "version": "0.34.52", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", - "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzc - "@simple-libs/stream-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", - "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", - "dev": true - }, "@sinclair/typebox": { "version": "0.34.52", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", @@ -21292,92 +20224,38 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "@types/mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/mysql": { - "version": "2.15.27", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", - "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz", - "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", - "requires": { - "undici-types": "~8.9.0" - }, - "dependencies": { - "undici-types": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", - "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==" - } - } - }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", + "dev": true + }, + "@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", "dev": true, "requires": { "@types/node": "*" } }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", - "dev": true, + "@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", "requires": { "@types/node": "*" } }, - "@types/node-int64": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.32.tgz", - "integrity": "sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==", - "dev": true, + "@types/node": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz", + "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", "requires": { - "@types/node": "*" + "undici-types": "~8.9.0" + }, + "dependencies": { + "undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==" + } } }, "@types/node-int64": { @@ -21551,66 +20429,20 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", - "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/type-utils": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "ignore": "^7.0.5", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", - "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1" - } - }, - "@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.70.1", - "eslint-visitor-keys": "^5.0.0" - } - }, - "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 - }, - "ignore": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", - "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", - "dev": true - }, - "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, - "requires": {} - } + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/parser": { @@ -21626,25 +20458,6 @@ "debug": "^4.3.4" } }, - "@typescript-eslint/project-service": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", - "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", - "dev": true, - "requires": { - "@typescript-eslint/tsconfig-utils": "^8.70.1", - "@typescript-eslint/types": "^8.70.1", - "debug": "^4.4.3" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true - } - } - }, "@typescript-eslint/scope-manager": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", @@ -21655,96 +20468,16 @@ "@typescript-eslint/visitor-keys": "7.18.0" } }, - "@typescript-eslint/tsconfig-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", - "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", - "dev": true, - "requires": {} - }, "@typescript-eslint/type-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", - "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", - "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", - "dev": true, - "requires": { - "@typescript-eslint/project-service": "8.70.1", - "@typescript-eslint/tsconfig-utils": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.70.1", - "eslint-visitor-keys": "^5.0.0" - } - }, - "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.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, - "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 - }, - "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" - } - }, - "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, - "requires": {} - } + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { @@ -21770,97 +20503,15 @@ } }, "@typescript-eslint/utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", - "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", - "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1" - } - }, - "@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", - "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", - "dev": true, - "requires": { - "@typescript-eslint/project-service": "8.70.1", - "@typescript-eslint/tsconfig-utils": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "8.70.1", - "eslint-visitor-keys": "^5.0.0" - } - }, - "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.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, - "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 - }, - "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" - } - }, - "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, - "requires": {} - } + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" } }, "@typescript-eslint/visitor-keys": { @@ -22355,12 +21006,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "argue-cli": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", - "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", - "dev": true - }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -23018,31 +21663,33 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "conventional-changelog-angular": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", - "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "requires": { - "@conventional-changelog/template": "^1.4.0" + "compare-func": "^2.0.0" } }, "conventional-changelog-conventionalcommits": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", - "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "requires": { - "@conventional-changelog/template": "^1.4.0" + "compare-func": "^2.0.0" } }, "conventional-commits-parser": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", - "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "requires": { - "@simple-libs/stream-utils": "^2.0.0", - "argue-cli": "^3.1.0" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" } }, "convert-source-map": { @@ -23126,6 +21773,12 @@ "which": "^2.0.1" } }, + "dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true + }, "data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -23381,12 +22034,6 @@ "hasown": "^2.0.2" } }, - "es-toolkit": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", - "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", - "dev": true - }, "esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -23819,13 +22466,6 @@ "bser": "2.1.1" } }, - "fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "requires": {} - }, "fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -24178,12 +22818,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "get-east-asian-width": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", - "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", - "dev": true - }, "get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -24222,6 +22856,17 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "requires": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + } + }, "glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -24275,12 +22920,12 @@ "dev": true }, "global-directory": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", - "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "requires": { - "ini": "6.0.0" + "ini": "4.1.1" } }, "globals": { @@ -24477,6 +23122,12 @@ "resolve-cwd": "^3.0.0" } }, + "import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -24505,9 +23156,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true }, "int53": { @@ -24610,6 +23261,15 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "requires": { + "text-extensions": "^2.0.0" + } + }, "is-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", @@ -25199,9 +23859,9 @@ "dev": true }, "joi": { - "version": "18.2.9", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", - "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz", + "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==", "requires": { "@hapi/address": "^5.1.1", "@hapi/formula": "^3.0.2", @@ -25298,6 +23958,22 @@ "universalify": "^2.0.0" } }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -25375,6 +24051,18 @@ "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", "dev": true }, + "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==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -25387,6 +24075,36 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -25497,6 +24215,12 @@ "fs-monkey": "^1.0.4" } }, + "meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true + }, "merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -26047,16 +24771,16 @@ "dev": true }, "pg": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", - "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", + "version": "8.13.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz", + "integrity": "sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ==", "requires": { - "pg-cloudflare": "^1.4.0", - "pg-connection-string": "^2.14.0", - "pg-pool": "^3.14.0", - "pg-protocol": "^1.16.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.1", + "pg-protocol": "^1.7.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" } }, "pg-cloudflare": { @@ -26068,7 +24792,7 @@ "pg-connection-string": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", - "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg== + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==" }, "pg-int8": { "version": "1.0.1", @@ -26082,9 +24806,9 @@ "requires": {} }, "pg-protocol": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", - "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==" + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==" }, "pg-types": { "version": "2.2.0", @@ -26101,10 +24825,7 @@ "pgpass": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.6.tgz", - "integrity": "sha512-lqIfH7bdgsxHAY/ZnUOwm+aCFKrsHBDhSFuk9O0B9uCqJAIkrKTo/+LQqLPLUS4e04+jCmQVikxE3QipH5chPw==", - "requires": { - "split2": "^4.1.0" - } + "integrity": "sha512-lqIfH7bdgsxHAY/ZnUOwm+aCFKrsHBDhSFuk9O0B9uCqJAIkrKTo/+LQqLPLUS4e04+jCmQVikxE3QipH5chPw==" }, "picocolors": { "version": "1.1.1", @@ -26778,7 +25499,8 @@ "split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true }, "sprintf-js": { "version": "1.0.3", @@ -27119,6 +25841,12 @@ } } }, + "text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -27147,33 +25875,11 @@ "dev": true }, "tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - - }, - "tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true }, - "tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "requires": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - } - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -27883,9 +26589,9 @@ "dev": true }, "zod": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", - "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==" + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" } } } diff --git a/package.json b/package.json index 9ea1a970..ebe1e052 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "dotenv": "^16.4.5", "helmet": "^7.1.0", "ioredis": "^6.0.0", - "joi": "^18.2.9", + "joi": "^18.2.3", "parquetjs": "^0.11.2", "pg": "8.23.0", "prom-client": "^15.1.3", @@ -75,15 +75,15 @@ "viem": "^2.44.4", "winston": "^3.13.0", "ws": "^8.18.0", - "zod": "^4.6.5", + "zod": "^3.23.8", "@msgpack/msgpack": "^3.0.0" }, "devDependencies": { - "@commitlint/cli": "^21.2.3", - "@commitlint/config-conventional": "^21.2.3", + "@commitlint/cli": "^19.8.1", + "@commitlint/config-conventional": "^19.8.1", "@nestjs/cli": "^11.0.24", "@nestjs/schematics": "^11.1.0", - "@nestjs/testing": "^12.1.0", + "@nestjs/testing": "^11.1.28", "@stryker-mutator/core": "^8.0.0", "@stryker-mutator/jest-runner": "^8.0.0", "@types/cors": "^2.8.17", @@ -95,7 +95,7 @@ "@types/uuid": "^10.0.0", "@types/ws": "^8.5.12", "@msgpack/msgpack": "^3.0.0", - "@typescript-eslint/eslint-plugin": "^8.70.1", + "@typescript-eslint/eslint-plugin": "^7.13.0", "@typescript-eslint/parser": "^7.13.0", "eslint": "^8.57.0", "fast-check": "^4.10.2", diff --git a/src/common/stellar-signature.ts b/src/common/stellar-signature.ts index 20fc23be..fdef0942 100644 --- a/src/common/stellar-signature.ts +++ b/src/common/stellar-signature.ts @@ -31,9 +31,6 @@ function canonicalPayload(payload: Record): stri function buildV2IntentMessage( context: IntentSignatureContext, action: "accept" | "fill" | "cancel", - /** - * Build the canonical message that a solver must sign to update their mutable - * profile fields (name / supportedChains / supportedTokens / avgFillTime). intentId: string, payload: Record, ): string { @@ -158,18 +155,6 @@ export function buildDisputeDecisionMessage(disputeId: string, resolution: strin return `dispute-decision:${disputeId}:${resolution}:${reason}`; } -/** - * Build the canonical message that a solver must sign to update their mutable - * profile fields (name / supportedChains / supportedTokens / avgFillTime). - * - * Signing over just the address is sufficient here: it proves control of the - * account whose profile is being edited, and the request body is already - * constrained by the DTO whitelist so no immutable field can ride along. - */ -export function buildUpdateSolverMessage(address: string): string { - return `update-solver:${address}`; -} - /** * Canonical message a solver signs to prove a fill landed in time and cancel * a pending slash during its challenge window (issue #397). diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 82819fe0..f8b52df5 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -278,6 +278,7 @@ export interface AppConfig { refreshIntervalMs: number; /** Comma-separated extra secrets: "name:envVar:required". */ extra: string; + }; /** WS gateway hardening (issue #455). */ ws: { /** Largest inbound frame accepted; larger frames close the socket (1009). */ @@ -467,6 +468,7 @@ export default (): AppConfig => ({ provider: (process.env.SECRETS_PROVIDER ?? "env") as "env" | "aws-secrets-manager" | "vault-kv", refreshIntervalMs: parseInt(process.env.SECRETS_REFRESH_INTERVAL_MS ?? "60000", 10), extra: process.env.SECRETS_EXTRA ?? "", + }, ws: { maxPayloadBytes: parseInt(process.env.WS_MAX_PAYLOAD_BYTES ?? "16384", 10), maxConnectionsPerIp: parseInt(process.env.WS_MAX_CONNECTIONS_PER_IP ?? "20", 10), diff --git a/src/governance/governance.module.ts b/src/governance/governance.module.ts index 0ebaac87..4477885c 100644 --- a/src/governance/governance.module.ts +++ b/src/governance/governance.module.ts @@ -1,5 +1,4 @@ import { forwardRef, Module } from "@nestjs/common"; -import { Module, forwardRef } from "@nestjs/common"; import { ProtocolParamsService } from "./params.service"; import { ParamsController } from "./params.controller"; import { SorobanModule } from "../soroban/soroban.module"; diff --git a/src/intents/dto/fill-intent.dto.ts b/src/intents/dto/fill-intent.dto.ts index 337f7a6f..52f839bc 100644 --- a/src/intents/dto/fill-intent.dto.ts +++ b/src/intents/dto/fill-intent.dto.ts @@ -1,7 +1,5 @@ import { IsInt, IsOptional, IsString, Matches, Max, MaxLength, Min, MinLength } from "class-validator"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsString, Matches, MaxLength, MinLength } from "class-validator"; -import { ApiProperty } from "@nestjs/swagger"; const ED25519_SIGNATURE_MAX_LENGTH = 88; diff --git a/src/intents/intents.gateway.spec.ts b/src/intents/intents.gateway.spec.ts index 8c23a751..b1ca0e18 100644 --- a/src/intents/intents.gateway.spec.ts +++ b/src/intents/intents.gateway.spec.ts @@ -37,8 +37,12 @@ function makeSolverRecord(address: string, overrides: Partial = {} }; } -/** Stub capability index: the gateway only reads eligible intents from it. */ -function makeIntentIndex(): IntentCapabilityIndex { +/** + * Capability index for tests: wraps the real index around `intentsService`, + * or returns a stub the gateway can read from when none is given. + */ +function makeIntentIndex(intentsService?: IntentsService): IntentCapabilityIndex { + if (intentsService) return new IntentCapabilityIndex(intentsService); return { rebuild: jest.fn().mockResolvedValue(undefined), addIntent: jest.fn(), @@ -86,10 +90,6 @@ function makeSolversService() { } as any; } -function makeIntentIndex(intentsService: IntentsService): IntentCapabilityIndex { - return new IntentCapabilityIndex(intentsService); -} - function makeFeed( intentsService: IntentsService, solversService: ReturnType, diff --git a/src/intents/intents.gateway.ts b/src/intents/intents.gateway.ts index c1eb2709..7d2a7b6f 100644 --- a/src/intents/intents.gateway.ts +++ b/src/intents/intents.gateway.ts @@ -20,7 +20,6 @@ import { Backplane, SequencedEvent, WS_BACKPLANE } from "./backplane/backplane.t import { MemoryBackplane } from "./backplane/memory.backplane"; import { ConnectionState, resolveClientIp, EncodingFormat } from "./ws/connection-state"; import { EncodingCache } from "./ws/encoding-cache"; -import { ConnectionState, resolveClientIp } from "./ws/connection-state"; import { IntentFeedService } from "./feed/intent-feed.service"; import { FeedClient, FeedFilter } from "./feed/feed.types"; import { randomUUID } from "node:crypto"; @@ -187,13 +186,6 @@ export class IntentsGateway } } - handleConnection(client: WebSocket) { - this.subscribers.set(client, { - chains: null, - solver: null, - wantAll: false, - subscriptionCount: 0, - }); /** * Send an event to a client using its negotiated encoding format (Activity 1). * @@ -492,12 +484,6 @@ export class IntentsGateway // all=true: opt out of capability filtering. if (msg.all === true) { - const existing = this.subscribers.get(client) ?? { - chains: null, - solver: null, - wantAll: false, - subscriptionCount: 0, - }; const existing = this.subscribers.get(client) ?? { chains: null, solver: null, wantAll: false, subscriptionCount: 0 }; this.subscribers.set(client, { ...existing, wantAll: true }); this.feed.updateClientFilter(feedClient, { ...current, wantAll: true }); @@ -925,20 +911,6 @@ export class IntentsGateway } } this.lastHeartbeatTerminatedCount = terminated; - if (terminated > 0) { - logger.debug( - `ws heartbeat terminated ${terminated} dead client(s) (subscribers=${this.subscribers.size})`, - ); - } - continue; - } - - feedClient.alive = false; - if (client.readyState === WebSocket.OPEN) { - client.ping(); - } - } - this.lastHeartbeatTerminatedCount = terminated; if (terminated > 0) { logger.debug( `ws heartbeat terminated ${terminated} dead client(s) (subscribers=${this.subscribers.size})`, diff --git a/src/intents/intents.service.shadow.spec.ts b/src/intents/intents.service.shadow.spec.ts index 1e76bec9..fa705c45 100644 --- a/src/intents/intents.service.shadow.spec.ts +++ b/src/intents/intents.service.shadow.spec.ts @@ -6,7 +6,6 @@ import { PrismaService } from "../prisma/prisma.service"; import { ProtocolParamsService } from "../governance/params.service"; import { ShadowService, type ShadowObservationRequest } from "../soroban/shadow.service"; import { StellarTxService } from "../soroban/stellar-tx.service"; -import { ProtocolParamsService } from "../governance/params.service"; import { IntentsService } from "./intents.service"; import { InMemoryIntentsRepository } from "./intents.repository"; import { MutationResult, VersionConflict } from "./intents.repository"; diff --git a/src/solvers/solvers.controller.ts b/src/solvers/solvers.controller.ts index 5e09dfcf..90e11054 100644 --- a/src/solvers/solvers.controller.ts +++ b/src/solvers/solvers.controller.ts @@ -31,10 +31,6 @@ import { import { isCanaryIntent } from "../common/canary"; import { AppConfig } from "../config/configuration"; import { SolversService, LeaderboardWindow } from "./solvers.service"; -import { ListIntentsDto } from "../intents/dto/list-intents.dto"; -import { AppConfig } from "../config/configuration"; -import { isCanaryIntent } from "../common/canary"; -import { IntentCapabilityIndex } from "../intents/solver-intent-matcher"; import { RegisterSolverDto } from "./dto/register-solver.dto"; import { UpdateSolverDto } from "./dto/update-solver.dto"; import { UpdateSolverStatusDto } from "./dto/update-solver-status.dto"; diff --git a/src/soroban/signer.service.spec.ts b/src/soroban/signer.service.spec.ts index 80df543d..d13afd70 100644 --- a/src/soroban/signer.service.spec.ts +++ b/src/soroban/signer.service.spec.ts @@ -4,20 +4,6 @@ import { AppConfig } from "../config/configuration"; import { SignerService } from "./signer.service"; import { SorobanService } from "./soroban.service"; import { ISigner } from "./signers/signer.interface"; -import { LocalKeypairSigner } from "./signers/local-keypair.signer"; -import { findSensitiveKeyMaterial } from "./redaction"; - -/** - * Builds a local-keypair signing backend over a stubbed config, i.e. the same - * ISigner the SIGNER_TOKEN provider hands to SignerService in the app. - */ -function signerWith(signerSecretKey: string, network: AppConfig["stellar"]["network"] = "testnet"): ISigner { - const values: Record = { - "stellar.signingKey": signerSecretKey, - "stellar.network": network, - }; - const configService = { get: (path: string) => values[path] } as ConfigService; - return new LocalKeypairSigner(configService); import { findSensitiveKeyMaterial } from "./redaction"; /** @@ -53,14 +39,11 @@ function fakeSorobanService(startingSequence = "100") { describe("SignerService", () => { it("reports unconfigured when no secret is set", () => { - const service = new SignerService(signerWith(""), fakeSorobanService()); const service = new SignerService(fakeSigner(), fakeSorobanService()); expect(service.isConfigured()).toBe(false); }); it("throws a clear, secret-free error when signing without a configured key", () => { - const service = new SignerService(signerWith(""), fakeSorobanService()); - expect(() => service.getPublicKey()).toThrow(/SOROBAN_SIGNING_KEY/); const service = new SignerService(fakeSigner(), fakeSorobanService()); expect(() => service.getPublicKey()).toThrow(/not configured|signing key/i); // The message must stay secret-free (no strkey / seed shape). @@ -75,7 +58,6 @@ describe("SignerService", () => { it("derives the public key from the configured secret", () => { const keypair = Keypair.random(); - const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); expect(service.isConfigured()).toBe(true); @@ -84,9 +66,6 @@ describe("SignerService", () => { it("maps network config to the right passphrase", () => { const soroban = fakeSorobanService(); - expect(new SignerService(signerWith("", "testnet"), soroban).getNetworkPassphrase()).toBe(Networks.TESTNET); - expect(new SignerService(signerWith("", "futurenet"), soroban).getNetworkPassphrase()).toBe(Networks.FUTURENET); - expect(new SignerService(signerWith("", "mainnet"), soroban).getNetworkPassphrase()).toBe(Networks.PUBLIC); expect(new SignerService(fakeSigner(undefined, "testnet"), soroban).getNetworkPassphrase()).toBe(Networks.TESTNET); expect(new SignerService(fakeSigner(undefined, "futurenet"), soroban).getNetworkPassphrase()).toBe(Networks.FUTURENET); expect(new SignerService(fakeSigner(undefined, "mainnet"), soroban).getNetworkPassphrase()).toBe(Networks.PUBLIC); @@ -94,7 +73,6 @@ describe("SignerService", () => { it("signs a transaction with the configured key", async () => { const keypair = Keypair.random(); - const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); const account = new Account(keypair.publicKey(), "1"); @@ -110,7 +88,6 @@ describe("SignerService", () => { it("never includes the raw secret in string/JSON/inspect representations", () => { const keypair = Keypair.random(); - const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService()); const service = new SignerService(fakeSigner(keypair), fakeSorobanService()); const secret = keypair.secret(); @@ -136,7 +113,6 @@ describe("SignerService", () => { it("fetches the starting sequence once and increments it locally", async () => { const keypair = Keypair.random(); const soroban = fakeSorobanService("100"); - const service = new SignerService(signerWith(keypair.secret()), soroban); const service = new SignerService(fakeSigner(keypair), soroban); const first = await service.withNextSequence(async (sequence) => sequence); @@ -150,7 +126,6 @@ describe("SignerService", () => { it("hands out a distinct, gap-free sequence to every concurrent caller", async () => { const keypair = Keypair.random(); const soroban = fakeSorobanService("0"); - const service = new SignerService(signerWith(keypair.secret()), soroban); const service = new SignerService(fakeSigner(keypair), soroban); const results = await Promise.all( @@ -164,7 +139,6 @@ describe("SignerService", () => { it("runs callers strictly one at a time, in call order", async () => { const keypair = Keypair.random(); - const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService("0")); const service = new SignerService(fakeSigner(keypair), fakeSorobanService("0")); const order: number[] = []; @@ -183,7 +157,6 @@ describe("SignerService", () => { it("drops the cached sequence after a failure so the next call re-syncs from the network", async () => { const keypair = Keypair.random(); const soroban = fakeSorobanService("100"); - const service = new SignerService(signerWith(keypair.secret()), soroban); const service = new SignerService(fakeSigner(keypair), soroban); await expect( @@ -199,7 +172,6 @@ describe("SignerService", () => { it("does not let a failed caller block callers queued behind it", async () => { const keypair = Keypair.random(); - const service = new SignerService(signerWith(keypair.secret()), fakeSorobanService("0")); const service = new SignerService(fakeSigner(keypair), fakeSorobanService("0")); const failing = service.withNextSequence(async () => { diff --git a/src/soroban/solver-registry.service.spec.ts b/src/soroban/solver-registry.service.spec.ts index 1f9a7379..bd5e5237 100644 --- a/src/soroban/solver-registry.service.spec.ts +++ b/src/soroban/solver-registry.service.spec.ts @@ -84,6 +84,7 @@ function makeConfigService( provider: "env", refreshIntervalMs: 60000, extra: "", + }, ws: { maxPayloadBytes: 16384, maxConnectionsPerIp: 20, diff --git a/src/soroban/soroban.service.ts b/src/soroban/soroban.service.ts index f22558a9..6817c4cb 100644 --- a/src/soroban/soroban.service.ts +++ b/src/soroban/soroban.service.ts @@ -64,13 +64,6 @@ export class SorobanService { } const ledger = body.result?.ledgers?.[0]; return ledger ? { header: { closeTime: ledger.ledgerCloseTime } } : {}; - getLedger(sequence: number) { - // The stellar-sdk 12.x Server type no longer exposes `getLedger`; the call - // is preserved for the event-ingestion lag metric. Cast to keep compiling - // against the pinned SDK — the runtime API may need a follow-up migration. - return (this.server as unknown as { - getLedger(seq: number): Promise<{ header?: { closeTime?: string | number } }>; - }).getLedger(sequence); } getEvents(request: SorobanRpc.Server.GetEventsRequest) { diff --git a/src/tokens/tokens.service.ts b/src/tokens/tokens.service.ts index 11ced880..90d7b396 100644 --- a/src/tokens/tokens.service.ts +++ b/src/tokens/tokens.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, Inject, Injectable } from "@nestjs/common"; import { SUPPORTED_TOKENS, StellarToken } from "./tokens.data"; -import { SUPPORTED_TOKENS, STELLAR_TOKENS, StellarToken } from "./tokens.data"; import { SupportedChain } from "../intents/intents.types"; import { ITokensRepository, TOKENS_REPOSITORY, TokenRecord } from "./tokens.repository"; diff --git a/src/treasury/treasury.service.spec.ts b/src/treasury/treasury.service.spec.ts index c1707433..9468f36b 100644 --- a/src/treasury/treasury.service.spec.ts +++ b/src/treasury/treasury.service.spec.ts @@ -4,21 +4,8 @@ import { TreasuryService } from "./treasury.service"; import { PrismaService } from "../prisma/prisma.service"; import { SorobanService } from "../soroban/soroban.service"; -/** - * The Prisma delegates this suite stubs. `jest.Mocked` is shallow, so the - * model delegates (objects, not methods) would keep their real Prisma types; - * this local shape keeps every stub a `jest.Mock` with `mockResolvedValue`. - */ -type MockPrisma = { - feeLedger: { create: jest.Mock; findMany: jest.Mock }; - slashLedger: { create: jest.Mock; findMany: jest.Mock }; - refundLedger: { create: jest.Mock; findMany: jest.Mock }; - treasurySnapshot: { upsert: jest.Mock; findMany: jest.Mock; findUnique: jest.Mock }; -}; - describe("TreasuryService", () => { let service: TreasuryService; - let prisma: MockPrisma; // The mock Prisma only carries the ledger delegates the service touches; // type as `any` so the per-method `mockResolvedValue` calls type-check // (jest.Mocked does not deep-transform nested Prisma delegates). diff --git a/src/treasury/treasury.service.ts b/src/treasury/treasury.service.ts index ea7d4c86..09b543d4 100644 --- a/src/treasury/treasury.service.ts +++ b/src/treasury/treasury.service.ts @@ -165,10 +165,6 @@ export class TreasuryService { const [code, issuer] = asset.split(":"); if (issuer) { const balance = account.balances.find( - (b) => - b.asset_type !== "native" && - "asset_code" in b && - "asset_issuer" in b && (b): b is typeof b & { asset_code: string; asset_issuer: string } => b.asset_type !== "native" && "asset_code" in b && diff --git a/test/__mocks__/@stellar/stellar-sdk.ts b/test/__mocks__/@stellar/stellar-sdk.ts index c830c445..68c2007a 100644 --- a/test/__mocks__/@stellar/stellar-sdk.ts +++ b/test/__mocks__/@stellar/stellar-sdk.ts @@ -92,7 +92,6 @@ export const Networks = real.Networks; export const StrKey = real.StrKey; export const Address = real.Address; export const Asset = real.Asset; -export const Horizon = real.Horizon; export const Contract = real.Contract; export const Account = real.Account; export const Operation = real.Operation; From 40c30e2be27b97e0b4d9e966d46276974f620c15 Mon Sep 17 00:00:00 2001 From: benedictworks-home Date: Wed, 30 Sep 2026 14:39:31 -0700 Subject: [PATCH 3/3] feat(auth): add SEP-10 challenge auth with short-lived EdDSA JWTs (#442) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement SEP-10-style challenge authentication for solver bots and a short-lived EdDSA (Ed25519) session JWT: - GET /api/v1/auth/challenge?account=G... issues a server-signed challenge (home-domain + web_auth_domain manageData ops, single-use 48-byte nonce, 5-minute timebounds). - POST /api/v1/auth/token exchanges the client-signed challenge for a 15-minute JWT with sub/role claims. Verification uses the SDK's readChallengeTx + verifyChallengeTxThreshold (medium threshold, floored at 1) against Horizon signers; the nonce is consumed atomically and only after signatures verify, so challenges are single-use but failed attempts cannot burn them. - Roles: admin (SEP10_ADMIN_ACCOUNTS) > solver (registered) > user. - Nonce store: process-local memory by default, shared Redis (SEP10_NONCE_STORE=redis) for multi-replica replay protection. - WS gateway and SolverJwtGuard accept the EdDSA token first and keep HS256 (AUTH_JWT_SECRET) as a fallback for backward compatibility. - Key material: challenge signing reuses STELLAR_SIGNER_SECRET_KEY; JWTs are signed with SEP10_JWT_SIGNING_KEY (Ed25519 PKCS#8, required in production, distinct from the Soroban signer key). New env vars: SEP10_HOME_DOMAIN, SEP10_JWT_SIGNING_KEY, SEP10_ADMIN_ACCOUNTS, SEP10_NONCE_STORE (env.validation.ts + all four .env*.example files). Tests: sep10.service.spec.ts (challenge conformance, roles, multisig threshold, replay, unknown nonce, expiry via fake timers, wrong home domain, unknown account, JWT helper + key-material edge cases) and solver-jwt.guard.spec.ts. Docs: solver-onboarding.md §2a. Lint/typecheck/tests were not run locally (no node_modules); local checks: stripTypeScriptTypes parse (62 files), duplicate-binding scan, env-drift port — all clean. --- .env.example | 14 + .env.mainnet.example | 13 + .env.staging.example | 13 + .env.testnet.example | 13 + docs/solver-onboarding.md | 106 ++++++ src/app.module.ts | 4 + src/auth/sep10/account-info.ts | 49 +++ .../sep10/dto/auth-challenge.query.dto.ts | 18 + src/auth/sep10/dto/auth-token.dto.ts | 20 + src/auth/sep10/nonce.store.ts | 79 ++++ src/auth/sep10/sep10-keys.ts | 117 ++++++ src/auth/sep10/sep10.controller.ts | 55 +++ src/auth/sep10/sep10.module.ts | 56 +++ src/auth/sep10/sep10.service.spec.ts | 356 ++++++++++++++++++ src/auth/sep10/sep10.service.ts | 201 ++++++++++ .../solver-jwt.guard.spec.ts | 124 ++++++ .../solver-credentials/solver-jwt.guard.ts | 25 +- src/common/jwt.ts | 89 ++++- src/config/configuration.ts | 13 + src/config/env.validation.ts | 19 + src/intents/intents.gateway.ts | 20 +- test/__mocks__/@stellar/stellar-sdk.ts | 3 + 22 files changed, 1396 insertions(+), 11 deletions(-) create mode 100644 src/auth/sep10/account-info.ts create mode 100644 src/auth/sep10/dto/auth-challenge.query.dto.ts create mode 100644 src/auth/sep10/dto/auth-token.dto.ts create mode 100644 src/auth/sep10/nonce.store.ts create mode 100644 src/auth/sep10/sep10-keys.ts create mode 100644 src/auth/sep10/sep10.controller.ts create mode 100644 src/auth/sep10/sep10.module.ts create mode 100644 src/auth/sep10/sep10.service.spec.ts create mode 100644 src/auth/sep10/sep10.service.ts create mode 100644 src/auth/solver-credentials/solver-jwt.guard.spec.ts diff --git a/.env.example b/.env.example index e3e38c57..758ffac0 100644 --- a/.env.example +++ b/.env.example @@ -412,6 +412,20 @@ WS_DRAIN_TIMEOUT_MS=25000 # Empty disables JWT auth on the WS gateway. AUTH_JWT_SECRET= +# ─── SEP-10 solver authentication (issue #442) ──────────────────────────────── +# Home domain bound into the challenge (` auth` manage-data operation); +# the same value is used for the challenge's `web_auth_domain` operation. +SEP10_HOME_DOMAIN=localhost +# Ed25519 PKCS#8 private key signing the 15-minute session JWTs — REQUIRED in +# production (generate: openssl genpkey -algorithm ed25519). Must be distinct +# from STELLAR_SIGNER_SECRET_KEY; empty uses an ephemeral dev-only key. +SEP10_JWT_SIGNING_KEY= +# Comma-separated G-addresses issued role=admin. Empty = nobody. +SEP10_ADMIN_ACCOUNTS= +# Single-use challenge nonce store: memory | redis. Use redis in multi-replica +# deployments so a captured challenge cannot be replayed on another replica. +SEP10_NONCE_STORE=memory + # ─── API keys & distributed rate limiting (issue #441) ───────────────────────── # How often (ms) the local rate-limiter fallback prunes expired window entries. # Only relevant during a Redis outage; keeps the fallback map bounded. diff --git a/.env.mainnet.example b/.env.mainnet.example index 9b7d6f81..fa8c48c3 100644 --- a/.env.mainnet.example +++ b/.env.mainnet.example @@ -291,6 +291,19 @@ WS_SLOW_CONSUMER_POLICY=drop_oldest # Empty disables JWT auth on the WS gateway. AUTH_JWT_SECRET= +# ─── SEP-10 solver authentication (issue #442) ──────────────────────────────── +# Home domain bound into the challenge (` auth` manage-data operation). +SEP10_HOME_DOMAIN=localhost +# Ed25519 PKCS#8 private key signing the 15-minute session JWTs — REQUIRED in +# production (generate: openssl genpkey -algorithm ed25519). Must be distinct +# from STELLAR_SIGNER_SECRET_KEY. +SEP10_JWT_SIGNING_KEY= +# Comma-separated G-addresses issued role=admin. Empty = nobody. +SEP10_ADMIN_ACCOUNTS= +# Single-use challenge nonce store: memory | redis. Use redis in multi-replica +# deployments so a captured challenge cannot be replayed on another replica. +SEP10_NONCE_STORE=memory + # ─── API keys & distributed rate limiting (issue #441) ───────────────────────── RATE_LIMIT_LOCAL_PRUNE_MS=60000 # Redis URL for the shared rate-limit window. Empty = bounded local limiter. diff --git a/.env.staging.example b/.env.staging.example index fa89b76d..be43e81a 100644 --- a/.env.staging.example +++ b/.env.staging.example @@ -178,6 +178,19 @@ WS_SLOW_CONSUMER_POLICY=drop_oldest # Empty disables JWT auth on the WS gateway. AUTH_JWT_SECRET= +# ─── SEP-10 solver authentication (issue #442) ──────────────────────────────── +# Home domain bound into the challenge (` auth` manage-data operation). +SEP10_HOME_DOMAIN=localhost +# Ed25519 PKCS#8 private key signing the 15-minute session JWTs — REQUIRED in +# production (generate: openssl genpkey -algorithm ed25519). Must be distinct +# from STELLAR_SIGNER_SECRET_KEY. +SEP10_JWT_SIGNING_KEY= +# Comma-separated G-addresses issued role=admin. Empty = nobody. +SEP10_ADMIN_ACCOUNTS= +# Single-use challenge nonce store: memory | redis. Use redis in multi-replica +# deployments so a captured challenge cannot be replayed on another replica. +SEP10_NONCE_STORE=memory + # ─── API keys & distributed rate limiting (issue #441) ───────────────────────── RATE_LIMIT_LOCAL_PRUNE_MS=60000 # Redis URL for the shared rate-limit window. Empty = bounded local limiter. diff --git a/.env.testnet.example b/.env.testnet.example index 304a7ce1..3eb470ed 100644 --- a/.env.testnet.example +++ b/.env.testnet.example @@ -184,6 +184,19 @@ WS_SLOW_CONSUMER_POLICY=drop_oldest # Empty disables JWT auth on the WS gateway. AUTH_JWT_SECRET= +# ─── SEP-10 solver authentication (issue #442) ──────────────────────────────── +# Home domain bound into the challenge (` auth` manage-data operation). +SEP10_HOME_DOMAIN=localhost +# Ed25519 PKCS#8 private key signing the 15-minute session JWTs — REQUIRED in +# production (generate: openssl genpkey -algorithm ed25519). Must be distinct +# from STELLAR_SIGNER_SECRET_KEY. +SEP10_JWT_SIGNING_KEY= +# Comma-separated G-addresses issued role=admin. Empty = nobody. +SEP10_ADMIN_ACCOUNTS= +# Single-use challenge nonce store: memory | redis. Use redis in multi-replica +# deployments so a captured challenge cannot be replayed on another replica. +SEP10_NONCE_STORE=memory + # ─── Health probes (issue #492) ────────────────────────────────────────────── # Roles served by this process (api, ws, worker); readiness checks follow them. SERVICE_ROLES=api,ws,worker diff --git a/docs/solver-onboarding.md b/docs/solver-onboarding.md index ad992d3a..a2347100 100644 --- a/docs/solver-onboarding.md +++ b/docs/solver-onboarding.md @@ -148,6 +148,112 @@ where $\text{SuccessRate} = \frac{\text{fillsCompleted}}{\text{fillsCompleted} + --- +## 2a. SEP-10 Bot Authentication (Short-Lived Session Tokens) + +Bots authenticate to the API and the WebSocket feed with a SEP-10-style +challenge instead of a long-lived shared secret. You request a challenge, +sign it with your solver account key(s), and exchange it for a **15-minute +EdDSA session token** (`issue #442`). Signed-message endpoints from +§3b (`accept`/`fill` signatures) are unchanged — this flow only governs +*connection* authentication (WebSocket feed, scoped credential endpoints). + +### Step 1 — Request a challenge + +``` +GET /api/v1/auth/challenge?account=G... +``` + +```bash +curl "$BACKEND_URL/api/v1/auth/challenge?account=$SOLVER_ADDRESS" +``` + +```json +{ + "transaction": "AAAAAgAAAAB...base64XDR...", + "network_passphrase": "Test SDF Network ; September 2015" +} +``` + +The transaction is a server-signed SEP-10 challenge: a `manageData` +operation ` auth` carrying a single-use 48-byte nonce (source = +your account), a `web_auth_domain` operation, and 5-minute time bounds. + +### Step 2 — Sign and exchange + +Sign the transaction with the account's key(s) — for multisig accounts, sign +with enough signers to meet the account's **medium threshold** (floored at +1) — then POST the signed XDR back: + +```bash +curl -X POST "$BACKEND_URL/api/v1/auth/token" \ + -H "Content-Type: application/json" \ + -d "{\"transaction\": \"$SIGNED_CHALLENGE_XDR\"}" +``` + +```json +{ + "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...", + "token_type": "Bearer", + "expires_in": 900 +} +``` + +The JWT carries `sub` (your G-address), `role` (`solver` when the account is +registered, `user` otherwise, `admin` for accounts listed in +`SEP10_ADMIN_ACCOUNTS`), `iat`, and `exp`. It is signed with the Ed25519 key +`SEP10_JWT_SIGNING_KEY` (never with the Soroban signer key). + +**Each challenge is single-use.** A captured challenge cannot be exchanged +twice; failed exchanges (bad signature, wrong threshold) do not consume it. +A challenge is valid for 5 minutes (+300s clock-drift grace); after that, +request a fresh one. There is no refresh token — simply re-run the flow when +you receive a `401` or `auth_error`. + +### Using the token + +- **REST**: `Authorization: Bearer ` on guarded endpoints (e.g. the + solver-scoped credential routes). +- **WebSocket**: pass the token at handshake as `ws://host/ws?token=` + (or an `Authorization: Bearer` header), **or** as the first message after + connecting: + +```json +{ "type": "auth", "token": "" } +``` + +On success the gateway binds your solver identity to the connection +(capability-filtered snapshot, `auth_ok`); on failure it replies with +`{ "type": "auth_error", "reason": "..." }`. Legacy HS256 tokens signed with +`AUTH_JWT_SECRET` remain accepted during migration. + +### Key management + +```bash +# Generate the JWT signing key (keep it out of version control): +openssl genpkey -algorithm ed25519 -out sep10-jwt.pem +# Base64 DER also works: +openssl genpkey -algorithm ed25519 | tail -n +2 | tr -d '\n' +``` + +| Env var | Purpose | +|---|---| +| `STELLAR_SIGNER_SECRET_KEY` | Server keypair that signs challenges (reuse of the existing Soroban signer config). | +| `SEP10_JWT_SIGNING_KEY` | Ed25519 PKCS#8 key signing the session JWTs. **Required in production.** | +| `SEP10_HOME_DOMAIN` | Home domain embedded in challenges (default `localhost`). | +| `SEP10_ADMIN_ACCOUNTS` | Comma-separated G-addresses issued `role=admin`. | +| `SEP10_NONCE_STORE` | `memory` (default, single replica) or `redis` (multi-replica replay protection). | + +Notes: + +- The client account must exist on the network (unfunded/pre-authorized + accounts are out of scope for solver auth). +- Deployments with more than one replica **must** set `SEP10_NONCE_STORE=redis`, + otherwise a challenge issued by one replica could be replayed on another. +- The challenge's `web_auth_domain` and ` auth` key both come from + `SEP10_HOME_DOMAIN`; set it to your real API domain in production. + +--- + ## 3. Bot Connection, Authentication & WebSocket Topics ### Connecting to the Real-Time Feed diff --git a/src/app.module.ts b/src/app.module.ts index d050f8b1..b40be53f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -23,6 +23,7 @@ import { GuardianStateModule } from "./governance/guardian-state.service"; import { DatasetsModule } from "./datasets/datasets.module"; import { AbuseModule } from "./abuse/abuse.module"; import { ApiKeysModule } from "./auth/api-keys/api-keys.module"; +import { Sep10Module } from "./auth/sep10/sep10.module"; import { TieredThrottleGuard } from "./auth/rate-limit/tiered-throttle.guard"; @Module({ @@ -44,6 +45,9 @@ import { TieredThrottleGuard } from "./auth/rate-limit/tiered-throttle.guard"; PrismaModule, // Issue #441 — API key tiers + distributed rate limiting. ApiKeysModule, + // Issue #442 — SEP-10 challenge auth: challenge/token endpoints plus the + // key material the WS gateway and JWT guards use to verify issued tokens. + Sep10Module, // @Global() — registers MetricsService / MetricsInterceptor / MetricsController // for the whole app. Must be imported once in the root module or the global // providers never become visible to other modules (e.g. IntentsSweeperService) diff --git a/src/auth/sep10/account-info.ts b/src/auth/sep10/account-info.ts new file mode 100644 index 00000000..52816bca --- /dev/null +++ b/src/auth/sep10/account-info.ts @@ -0,0 +1,49 @@ +import { Horizon, WebAuth } from "@stellar/stellar-sdk"; + +/** Injection token for the {@link Sep10AccountInfoLoader} provider. */ +export const SEP10_ACCOUNT_INFO_LOADER = "SEP10_ACCOUNT_INFO_LOADER"; + +/** + * A signer of the SEP-10 client account (issue #442) — `{ key, weight, type }` + * exactly as the SDK's `verifyChallengeTxThreshold` signer summary expects + * (derived from that function's signature so the two can never drift). + * Signers that are not address-strkey prefixed are ignored by the SDK. + */ +export type Sep10Signer = Parameters[4][number]; + +/** On-chain account facts the SEP-10 exchange needs (issue #442). */ +export interface Sep10AccountInfo { + /** + * Weighted-signature threshold a challenge must meet. Implementations use + * the account's medium threshold, floored at 1: an account whose thresholds + * are all zero (a freshly created account) must still prove possession of a + * signing key rather than passing an unsigned challenge. + */ + threshold: number; + signers: Sep10Signer[]; +} + +/** + * Loads `thresholds` + `signers` for the client account that must verify a + * SEP-10 challenge. Kept behind an interface so tests inject fixtures and + * never touch the network (issue #442). + */ +export interface Sep10AccountInfoLoader { + load(account: string): Promise; +} + +/** + * Horizon-backed loader: `GET /accounts/{id}` is the canonical source of an + * account's signers and thresholds (issue #442). + */ +export class HorizonAccountInfoLoader implements Sep10AccountInfoLoader { + constructor(private readonly horizonUrl: string) {} + + async load(account: string): Promise { + const record = await new Horizon.Server(this.horizonUrl).loadAccount(account); + return { + threshold: Math.max(1, Number(record.thresholds.medium)), + signers: record.signers, + }; + } +} diff --git a/src/auth/sep10/dto/auth-challenge.query.dto.ts b/src/auth/sep10/dto/auth-challenge.query.dto.ts new file mode 100644 index 00000000..78d5b5d2 --- /dev/null +++ b/src/auth/sep10/dto/auth-challenge.query.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsDefined, Matches } from "class-validator"; + +/** + * Query string for `GET /api/v1/auth/challenge` (issue #442): the Stellar + * account that wants a SEP-10 challenge. Only G-addresses are accepted — + * muxed accounts and non-Stellar identifiers are out of scope for solver + * authentication. + */ +export class AuthChallengeQueryDto { + @ApiProperty({ + description: "Stellar account (G-address) requesting the challenge", + example: "GCEZWKCA3VHXBUJ4FBU6K3ZV4QX5Y4QZ4Y4QX5Y4QX5Y4QX5Y4QX5Y4Q", + }) + @IsDefined({ message: "account query parameter is required" }) + @Matches(/^G[A-Z2-7]{55}$/, { message: "account must be a Stellar G-address" }) + account!: string; +} diff --git a/src/auth/sep10/dto/auth-token.dto.ts b/src/auth/sep10/dto/auth-token.dto.ts new file mode 100644 index 00000000..97a9a479 --- /dev/null +++ b/src/auth/sep10/dto/auth-token.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsDefined, IsString, MinLength } from "class-validator"; + +/** + * Body for `POST /api/v1/auth/token` (issue #442): the server-issued SEP-10 + * challenge transaction, signed by the client account, as base64 XDR. The + * client account is read from the transaction itself; the exchange rejects + * challenges whose client account does not exist on the network or whose + * signer weight does not meet the account threshold. + */ +export class AuthTokenDto { + @ApiProperty({ + description: "Base64 XDR of the SEP-10 challenge transaction, signed by the client account", + example: "AAAAAgAAAAB...", + }) + @IsDefined({ message: "transaction is required" }) + @IsString() + @MinLength(64) + transaction!: string; +} diff --git a/src/auth/sep10/nonce.store.ts b/src/auth/sep10/nonce.store.ts new file mode 100644 index 00000000..d4d96ffe --- /dev/null +++ b/src/auth/sep10/nonce.store.ts @@ -0,0 +1,79 @@ +import Redis from "ioredis"; + +/** Injection token for {@link Sep10NonceStore}. */ +export const SEP10_NONCE_STORE = "SEP10_NONCE_STORE"; + +/** + * Single-use store for SEP-10 challenge nonces (issue #442). + * + * The 48-byte nonce inside a challenge's `manageData` operation makes a + * challenge unique; this store makes it *single-use*. `remember` is called + * when the challenge is issued and `consume` atomically claims it when the + * signed challenge is exchanged, so a captured challenge can never be + * replayed for a second token. + */ +export interface Sep10NonceStore { + /** Records a freshly issued nonce, expiring it after `ttlSeconds`. */ + remember(nonce: string, ttlSeconds: number): Promise; + /** + * Atomically claims `nonce`. Returns true only for the first consumer; + * false when the nonce was never issued or was already used. + */ + consume(nonce: string): Promise; +} + +/** + * Process-local nonce store for dev/test and single-replica deployments + * without Redis. Expired entries are pruned opportunistically on writes. + */ +export class InMemorySep10NonceStore implements Sep10NonceStore { + private readonly nonces = new Map(); + + async remember(nonce: string, ttlSeconds: number): Promise { + const now = Date.now(); + this.prune(now); + this.nonces.set(nonce, now + ttlSeconds * 1000); + } + + async consume(nonce: string): Promise { + const expiresAt = this.nonces.get(nonce); + this.nonces.delete(nonce); + return expiresAt !== undefined && expiresAt > Date.now(); + } + + private prune(nowMs: number): void { + for (const [key, expiresAt] of this.nonces) { + if (expiresAt <= nowMs) this.nonces.delete(key); + } + } +} + +/** + * Redis-backed nonce store shared by every replica (issue #442). + * + * Consumption runs a tiny Lua script so GET + DEL is a single atomic step — + * two replicas exchanging the same challenge concurrently can only ever + * succeed once. Keys carry an expiry so expired nonces cost nothing. + */ +export class RedisSep10NonceStore implements Sep10NonceStore { + /** GET the nonce and delete it in one script; 1 when it existed, 0 otherwise. */ + private static readonly CONSUME_SCRIPT = + 'local v = redis.call("GET", KEYS[1]) if v then redis.call("DEL", KEYS[1]) end return (v and 1) or 0'; + + constructor(private readonly redis: Redis) {} + + async remember(nonce: string, ttlSeconds: number): Promise { + // setex overwrites are harmless: nonces are 48 random bytes per challenge + // and each buildChallenge issues a fresh one. + await this.redis.setex(this.key(nonce), ttlSeconds, "1"); + } + + async consume(nonce: string): Promise { + const result: unknown = await this.redis.eval(RedisSep10NonceStore.CONSUME_SCRIPT, 1, this.key(nonce)); + return Number(result) === 1; + } + + private key(nonce: string): string { + return `sep10:nonce:${nonce}`; + } +} diff --git a/src/auth/sep10/sep10-keys.ts b/src/auth/sep10/sep10-keys.ts new file mode 100644 index 00000000..8c44f0da --- /dev/null +++ b/src/auth/sep10/sep10-keys.ts @@ -0,0 +1,117 @@ +import { Keypair } from "@stellar/stellar-sdk"; +import { generateKeyPairSync, type KeyObject } from "node:crypto"; +import { ed25519PublicKeyFor, loadEd25519PrivateKey } from "../../common/jwt"; +import { logger } from "../../common/logger"; + +/** Injection token for {@link Sep10KeyMaterial}. */ +export const SEP10_KEYS = "SEP10_KEYS"; + +/** + * The key material the SEP-10 flow (issue #442) needs in one place: + * + * - `challenge` signs the server half of every challenge transaction + * (`STELLAR_SIGNER_SECRET_KEY`, ephemeral in dev when unset). + * - `jwtPrivateKey` signs the short-lived session JWTs (`SEP10_JWT_SIGNING_KEY`, + * an Ed25519 key that must be distinct from the Soroban signer key; + * ephemeral in dev when unset). + * + * The material is resolved once per process so the issuing service and the + * WebSocket guard verify with the *same* key even when only an ephemeral + * (dev) key is configured. Production deployments must set + * `SEP10_JWT_SIGNING_KEY`, otherwise tokens do not survive a restart or a + * replica change — `env.validation.ts` enforces that. + */ +export interface Sep10KeyMaterial { + /** Server keypair that signs SEP-10 challenge transactions. */ + readonly challenge: Keypair; + /** Ed25519 private key that signs issued session JWTs. */ + readonly jwtPrivateKey: KeyObject; + /** Public half of `jwtPrivateKey`, used to verify issued JWTs. */ + readonly jwtPublicKey: KeyObject; + /** True when either key had to be generated for this process (dev only). */ + readonly ephemeral: boolean; +} + +let cached: Sep10KeyMaterial | null = null; + +/** + * Resolves (and caches) the process-wide SEP-10 key material from the two + * configured secrets. Throws when a non-empty value is not a usable key so a + * production misconfiguration fails at boot rather than silently issuing + * tokens nobody can verify. + * + * @param stellarSignerSecret - `STELLAR_SIGNER_SECRET_KEY`, or "" for an + * ephemeral challenge keypair (dev/test only). + * @param jwtSigningKey - `SEP10_JWT_SIGNING_KEY` (Ed25519 PKCS#8 PEM or + * base64 DER), or "" for an ephemeral key (dev/test only). + */ +export function sep10Keys(stellarSignerSecret: string, jwtSigningKey: string): Sep10KeyMaterial { + if (cached) return cached; + + let challenge: Keypair; + const seed = stellarSignerSecret.trim(); + if (seed) { + try { + challenge = Keypair.fromSecret(seed); + } catch { + throw new Error("STELLAR_SIGNER_SECRET_KEY is not a valid Stellar secret seed"); + } + } else { + challenge = Keypair.random(); + logger.warn( + "SEP-10: STELLAR_SIGNER_SECRET_KEY is empty — using an ephemeral challenge key; " + + "challenges signed by this process will not verify after a restart", + ); + } + + const provided = jwtSigningKey.trim(); + let jwtPrivateKey: KeyObject; + let ephemeral = false; + if (provided) { + const loaded = loadEd25519PrivateKey(provided); + if (!loaded) { + throw new Error( + "SEP10_JWT_SIGNING_KEY is not a usable Ed25519 PKCS#8 private key " + + "(generate one with: openssl genpkey -algorithm ed25519)", + ); + } + jwtPrivateKey = loaded; + } else { + jwtPrivateKey = generateKeyPairSync("ed25519").privateKey; + ephemeral = true; + logger.warn( + "SEP-10: SEP10_JWT_SIGNING_KEY is empty — using an ephemeral JWT key; " + + "issued tokens will not verify after a restart or on another replica", + ); + } + + cached = { + challenge, + jwtPrivateKey, + jwtPublicKey: ed25519PublicKeyFor(jwtPrivateKey), + ephemeral: ephemeral || seed === "", + }; + return cached; +} + +/** Clears the cached key material (test hook; production never calls this). */ +export function resetSep10Keys(): void { + cached = null; +} + +/** + * Resolves just the Ed25519 public key needed to *verify* issued session JWTs + * (issue #442), from the same two config values the Sep10Module factory uses. + * + * Returns null when a non-empty signing key is unusable — verification then + * falls back to HS256 only. The Sep10Module factory still fails boot in that + * configuration, so this path only surfaces in tests and direct constructions. + */ +export function sep10JwtPublicKey(stellarSignerSecret: string, jwtSigningKey: string): KeyObject | null { + try { + return sep10Keys(stellarSignerSecret, jwtSigningKey).jwtPublicKey; + } catch (error) { + logger.warn(`SEP-10 JWT verification unavailable — ${(error as Error).message}`); + return null; + } +} diff --git a/src/auth/sep10/sep10.controller.ts b/src/auth/sep10/sep10.controller.ts new file mode 100644 index 00000000..3527b60a --- /dev/null +++ b/src/auth/sep10/sep10.controller.ts @@ -0,0 +1,55 @@ +import { Body, Controller, Get, HttpCode, Post, Query } from "@nestjs/common"; +import { ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags } from "@nestjs/swagger"; +import { AuthChallengeQueryDto } from "./dto/auth-challenge.query.dto"; +import { AuthTokenDto } from "./dto/auth-token.dto"; +import { Sep10Service } from "./sep10.service"; + +/** + * SEP-10-style challenge authentication (issue #442). + * + * `GET /api/v1/auth/challenge?account=G...` issues the server-signed + * challenge; `POST /api/v1/auth/token` exchanges the client-signed challenge + * for a 15-minute EdDSA session JWT used as a Bearer token on REST and as + * the handshake/first-message token on the WebSocket gateway. + */ +@ApiTags("auth") +@Controller("api/v1/auth") +export class Sep10Controller { + constructor(private readonly sep10: Sep10Service) {} + + /** + * Issues a SEP-10 challenge transaction bound to this server (home domain, + * `web_auth_domain` operation, single-use nonce, 5-minute time bounds). + */ + @Get("challenge") + @ApiOperation({ + summary: "Issue a SEP-10 challenge transaction", + description: + "Returns a base64 challenge transaction for the given Stellar account. " + + "Sign it with the account's key(s) and POST it to /api/v1/auth/token.", + }) + @ApiOkResponse({ description: "Challenge transaction issued" }) + @ApiBadRequestResponse({ description: "account is not a valid Stellar G-address" }) + challenge(@Query() query: AuthChallengeQueryDto) { + return this.sep10.buildChallenge(query.account); + } + + /** + * Exchanges a signed challenge for the short-lived session JWT. A challenge + * can be exchanged exactly once; refreshing means re-running the flow. + */ + @Post("token") + @HttpCode(200) + @ApiOperation({ + summary: "Exchange a signed SEP-10 challenge for a session JWT", + description: + "Verifies the server signature, time bounds, home domain, client signature " + + "weight against the account threshold, and the single-use nonce, then " + + "returns a 15-minute EdDSA JWT with `sub` and `role` claims.", + }) + @ApiOkResponse({ description: "Session JWT issued" }) + @ApiBadRequestResponse({ description: "challenge failed verification or was already used" }) + token(@Body() dto: AuthTokenDto) { + return this.sep10.exchange(dto.transaction); + } +} diff --git a/src/auth/sep10/sep10.module.ts b/src/auth/sep10/sep10.module.ts new file mode 100644 index 00000000..9657388f --- /dev/null +++ b/src/auth/sep10/sep10.module.ts @@ -0,0 +1,56 @@ +import { Module } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import Redis from "ioredis"; +import type { AppConfig } from "../../config/configuration"; +import { SolversModule } from "../../solvers/solvers.module"; +import { HorizonAccountInfoLoader, SEP10_ACCOUNT_INFO_LOADER } from "./account-info"; +import { + InMemorySep10NonceStore, + RedisSep10NonceStore, + SEP10_NONCE_STORE, + type Sep10NonceStore, +} from "./nonce.store"; +import { Sep10Controller } from "./sep10.controller"; +import { Sep10Service } from "./sep10.service"; +import { SEP10_KEYS, sep10Keys } from "./sep10-keys"; + +/** + * SEP-10 challenge authentication (issue #442): the challenge/token + * endpoints, the single-use nonce store (`SEP10_NONCE_STORE=redis` for + * shared replay protection, process-local `memory` otherwise), the Horizon + * signer/threshold loader, and the process-wide challenge/JWT key material. + */ +@Module({ + imports: [SolversModule], + controllers: [Sep10Controller], + providers: [ + Sep10Service, + { + provide: SEP10_KEYS, + inject: [ConfigService], + useFactory: (config: ConfigService) => + sep10Keys( + config.get("stellar.signerSecretKey", { infer: true }), + config.get("sep10JwtSigningKey", { infer: true }), + ), + }, + { + provide: SEP10_NONCE_STORE, + inject: [ConfigService], + useFactory: (config: ConfigService): Sep10NonceStore => { + if (config.get("sep10NonceStore", { infer: true }) === "redis") { + return new RedisSep10NonceStore(new Redis(config.get("redisUrl", { infer: true }))); + } + return new InMemorySep10NonceStore(); + }, + }, + { + provide: SEP10_ACCOUNT_INFO_LOADER, + inject: [ConfigService], + useFactory: (config: ConfigService) => + new HorizonAccountInfoLoader(config.get("stellar.horizonUrl", { infer: true })), + }, + ], + exports: [Sep10Service], +}) +export class Sep10Module {} diff --git a/src/auth/sep10/sep10.service.spec.ts b/src/auth/sep10/sep10.service.spec.ts new file mode 100644 index 00000000..e36bec74 --- /dev/null +++ b/src/auth/sep10/sep10.service.spec.ts @@ -0,0 +1,356 @@ +/** + * SEP-10 challenge authentication conformance and security tests (issue #442). + * + * Unit tests map no mock for `@stellar/stellar-sdk`, so these run against the + * genuine challenge builder/verifier: challenge shape, threshold/multisig + * verification, single-use nonces, expiry, and the EdDSA session JWTs. + */ +import { BadRequestException } from "@nestjs/common"; +import type { ConfigService } from "@nestjs/config"; +import { Keypair, Transaction, WebAuth } from "@stellar/stellar-sdk"; +import { generateKeyPairSync, createPublicKey } from "node:crypto"; +import { NETWORK_PASSPHRASES, type AppConfig } from "../../config/configuration"; +import { loadEd25519PrivateKey, signEddsaJwt, signHs256Jwt, verifyEddsaJwt } from "../../common/jwt"; +import type { SolversService } from "../../solvers/solvers.service"; +import type { Sep10AccountInfo, Sep10AccountInfoLoader } from "./account-info"; +import { InMemorySep10NonceStore, type Sep10NonceStore } from "./nonce.store"; +import { resetSep10Keys, sep10Keys, type Sep10KeyMaterial } from "./sep10-keys"; +import { + Sep10Service, + SEP10_CHALLENGE_TIMEOUT_SECONDS, + SEP10_JWT_TTL_SECONDS, + type Sep10Role, +} from "./sep10.service"; + +const HOME = "vortex.example.com"; +const PASSPHRASE = NETWORK_PASSPHRASES.testnet; + +/** Server + client keys for the flow; JWT material is independent of them. */ +const server = Keypair.random(); +const client = Keypair.random(); +const jwtMaterial = generateKeyPairSync("ed25519"); +const keys: Sep10KeyMaterial = { + challenge: server, + jwtPrivateKey: jwtMaterial.privateKey, + jwtPublicKey: createPublicKey(jwtMaterial.privateKey), + ephemeral: false, +}; + +/** Registered solvers (role "solver") by address. */ +const registered = new Map(); +const solvers = { + get: async (address: string) => registered.get(address), +} as unknown as SolversService; + +/** On-chain account fixtures by address; unknown accounts throw a 404. */ +const accounts = new Map(); +const loader: Sep10AccountInfoLoader = { + load: async (address) => { + const info = accounts.get(address); + if (!info) { + const error = new Error("Not Found") as Error & { response?: { status: number } }; + error.response = { status: 404 }; + throw error; + } + return info; + }, +}; + +function fakeConfig(overrides: Record = {}): ConfigService { + const values: Record = { + sep10HomeDomain: HOME, + sep10AdminAccounts: "", + "stellar.network": "testnet", + ...overrides, + }; + return { get: (key: string) => values[key] } as unknown as ConfigService; +} + +interface ServiceOverrides { + adminAccounts?: string; + homeDomain?: string; + nonces?: Sep10NonceStore; + loader?: Sep10AccountInfoLoader; + solvers?: unknown; +} + +function makeService(overrides: ServiceOverrides = {}) { + const nonces = overrides.nonces ?? new InMemorySep10NonceStore(); + const service = new Sep10Service( + fakeConfig({ + sep10AdminAccounts: overrides.adminAccounts ?? "", + sep10HomeDomain: overrides.homeDomain ?? HOME, + }), + nonces, + (overrides.solvers ?? solvers) as SolversService, + keys, + overrides.loader ?? loader, + ); + return { service, nonces }; +} + +/** Signs a challenge on the client side (one or more account signers). */ +function signChallenge(challengeXdr: string, ...signers: Keypair[]): string { + const tx = new Transaction(challengeXdr, PASSPHRASE); + for (const signer of signers) tx.sign(signer); + return tx.toEnvelope().toXDR("base64").toString(); +} + +/** Account fixture: the given key(s) as signers, weight summing to `threshold`. */ +function multiSigner(threshold: number, ...signers: Keypair[]): Sep10AccountInfo { + return { + threshold, + signers: signers.map((signer) => ({ + key: signer.publicKey(), + weight: 1, + type: "ed25519_public_key", + })), + }; +} + +async function expectRejected(promise: Promise, match: RegExp): Promise { + await promise.then( + () => { + throw new Error("expected the promise to reject"); + }, + (error: unknown) => { + expect(error).toBeInstanceOf(BadRequestException); + expect(String((error as Error).message)).toMatch(match); + }, + ); +} + +beforeEach(() => { + registered.clear(); + accounts.clear(); + accounts.set(client.publicKey(), multiSigner(1, client)); +}); + +describe("Sep10Service.buildChallenge", () => { + it("issues a SEP-10-conformant challenge bound to this server", async () => { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + + expect(challenge.network_passphrase).toBe(PASSPHRASE); + const tx = new Transaction(challenge.transaction, PASSPHRASE); + expect(tx.source).toBe(server.publicKey()); + expect(tx.sequence).toBe("0"); + const bounds = tx.timeBounds; + expect(Number(bounds?.maxTime) - Number(bounds?.minTime)).toBe(SEP10_CHALLENGE_TIMEOUT_SECONDS); + + const [nonceOp, domainOp] = tx.operations; + if (nonceOp?.type !== "manageData") throw new Error(`expected manageData, got ${nonceOp?.type}`); + if (domainOp?.type !== "manageData") throw new Error(`expected manageData, got ${domainOp?.type}`); + expect(nonceOp.name).toBe(`${HOME} auth`); + expect(nonceOp.source).toBe(client.publicKey()); + expect(domainOp.name).toBe("web_auth_domain"); + expect(tx.operations).toHaveLength(2); + }); + + it("produces a challenge the SDK itself accepts (server sig, domains, nonce)", async () => { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + const read = WebAuth.readChallengeTx(challenge.transaction, server.publicKey(), PASSPHRASE, HOME, HOME); + expect(read.clientAccountID).toBe(client.publicKey()); + expect(read.matchedHomeDomain).toBe(HOME); + }); + + it("rejects non-G addresses", async () => { + const { service } = makeService(); + await expectRejected(service.buildChallenge("not-a-stellar-address"), /G-address/); + }); +}); + +describe("Sep10Service.exchange", () => { + it("issues a 15-minute EdDSA JWT with sub and role claims", async () => { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + const result = await service.exchange(signChallenge(challenge.transaction, client)); + + expect(result.token_type).toBe("Bearer"); + expect(result.expires_in).toBe(SEP10_JWT_TTL_SECONDS); + + const header = JSON.parse(Buffer.from(result.token.split(".")[0] ?? "", "base64url").toString("utf8")); + expect(header.alg).toBe("EdDSA"); + + const claims = verifyEddsaJwt(result.token, keys.jwtPublicKey); + expect(claims).not.toBeNull(); + expect(claims?.sub).toBe(client.publicKey()); + expect(claims?.role).toBe("user"); + expect(Number(claims?.exp) - Number(claims?.iat)).toBe(SEP10_JWT_TTL_SECONDS); + }); + + it("grants role=solver for registered solver accounts", async () => { + registered.set(client.publicKey(), { address: client.publicKey(), isActive: true }); + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + const result = await service.exchange(signChallenge(challenge.transaction, client)); + expect(verifyEddsaJwt(result.token, keys.jwtPublicKey)?.role).toBe("solver" satisfies Sep10Role); + }); + + it("grants role=admin for SEP10_ADMIN_ACCOUNTS entries (comma list)", async () => { + const other = Keypair.random().publicKey(); + const { service } = makeService({ adminAccounts: `${other}, ${client.publicKey()}` }); + const challenge = await service.buildChallenge(client.publicKey()); + const result = await service.exchange(signChallenge(challenge.transaction, client)); + expect(verifyEddsaJwt(result.token, keys.jwtPublicKey)?.role).toBe("admin" satisfies Sep10Role); + }); + + it("accepts a multi-signature challenge once the threshold is met", async () => { + const account = Keypair.random(); + const signerA = Keypair.random(); + const signerB = Keypair.random(); + accounts.set(account.publicKey(), multiSigner(2, signerA, signerB)); + const { service } = makeService(); + + const challenge = await service.buildChallenge(account.publicKey()); + // Weight 1 < threshold 2 → rejected, and the nonce must NOT be burned by + // the failed attempt (verification happens before consumption). + await expectRejected(service.exchange(signChallenge(challenge.transaction, signerA)), /challenge rejected/); + // Both signers → weight 2 meets the threshold. + const result = await service.exchange(signChallenge(challenge.transaction, signerA, signerB)); + expect(verifyEddsaJwt(result.token, keys.jwtPublicKey)?.sub).toBe(account.publicKey()); + }); + + it("rejects a challenge signed by an unknown key", async () => { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + await expectRejected( + service.exchange(signChallenge(challenge.transaction, Keypair.random())), + /challenge rejected/, + ); + }); + + it("rejects a replayed challenge (single-use nonce)", async () => { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + const signed = signChallenge(challenge.transaction, client); + await service.exchange(signed); + await expectRejected(service.exchange(signed), /nonce was not issued by this server/); + }); + + it("rejects a challenge whose nonce was never issued", async () => { + const { service } = makeService(); + const foreign = WebAuth.buildChallengeTx( + server, + client.publicKey(), + HOME, + SEP10_CHALLENGE_TIMEOUT_SECONDS, + PASSPHRASE, + HOME, + ); + await expectRejected(service.exchange(signChallenge(foreign, client)), /nonce was not issued by this server/); + }); + + it("rejects an expired challenge", async () => { + jest.useFakeTimers(); + jest.setSystemTime(new Date("2026-01-01T00:00:00.000Z")); + try { + const { service } = makeService(); + const challenge = await service.buildChallenge(client.publicKey()); + const signed = signChallenge(challenge.transaction, client); + // Past maxTime plus the SDK's 300s clock-drift grace. + jest.setSystemTime(Date.now() + (SEP10_CHALLENGE_TIMEOUT_SECONDS + 350) * 1000); + await expectRejected(service.exchange(signed), /challenge rejected/); + } finally { + jest.useRealTimers(); + } + }); + + it("rejects a challenge minted for a different home domain", async () => { + const { service } = makeService(); + const foreign = WebAuth.buildChallengeTx( + server, + client.publicKey(), + "evil.example", + SEP10_CHALLENGE_TIMEOUT_SECONDS, + PASSPHRASE, + "evil.example", + ); + await expectRejected(service.exchange(signChallenge(foreign, client)), /challenge rejected/); + }); + + it("rejects challenges for accounts that do not exist on the network", async () => { + const ghost = Keypair.random(); + const { service } = makeService(); + const challenge = await service.buildChallenge(ghost.publicKey()); + await expectRejected(service.exchange(signChallenge(challenge.transaction, ghost)), /does not exist/); + }); +}); + +describe("verifyEddsaJwt", () => { + const now = 1_760_000_000; + const otherMaterial = generateKeyPairSync("ed25519"); + + it("accepts a freshly signed token", () => { + const token = signEddsaJwt({ sub: client.publicKey(), role: "user", iat: now, exp: now + 60 }, keys.jwtPrivateKey); + expect(verifyEddsaJwt(token, keys.jwtPublicKey, now)).not.toBeNull(); + }); + + it("rejects HS256 tokens (algorithm downgrade)", () => { + const token = signHs256Jwt({ sub: client.publicKey(), exp: now + 60 }, "a-shared-secret"); + expect(verifyEddsaJwt(token, keys.jwtPublicKey, now)).toBeNull(); + }); + + it("rejects expired tokens", () => { + const token = signEddsaJwt({ sub: client.publicKey(), role: "user", exp: now - 1 }, keys.jwtPrivateKey); + expect(verifyEddsaJwt(token, keys.jwtPublicKey, now)).toBeNull(); + }); + + it("rejects tokens signed with another key", () => { + const token = signEddsaJwt({ sub: client.publicKey(), role: "user", exp: now + 60 }, otherMaterial.privateKey); + expect(verifyEddsaJwt(token, keys.jwtPublicKey, now)).toBeNull(); + }); + + it("rejects tampered payloads", () => { + const token = signEddsaJwt({ sub: client.publicKey(), role: "user", exp: now + 60 }, keys.jwtPrivateKey); + const [header, , signature] = token.split("."); + const forged = Buffer.from(JSON.stringify({ sub: client.publicKey(), role: "admin", exp: now + 60 })).toString( + "base64url", + ); + expect(verifyEddsaJwt(`${header}.${forged}.${signature}`, keys.jwtPublicKey, now)).toBeNull(); + }); +}); + +describe("SEP-10 key material", () => { + const material = generateKeyPairSync("ed25519"); + const pem = material.privateKey.export({ type: "pkcs8", format: "pem" }) as string; + + afterEach(() => resetSep10Keys()); + + it("loads PEM, literal-newline PEM, and bare base64 DER", () => { + expect(loadEd25519PrivateKey(pem)).not.toBeNull(); + expect(loadEd25519PrivateKey(pem.replace(/\n/g, "\\n"))).not.toBeNull(); + const der = pem + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replace(/\s+/g, ""); + const fromDer = loadEd25519PrivateKey(der); + expect(fromDer).not.toBeNull(); + if (!fromDer) throw new Error("expected base64 DER to load"); + // Same key in all three encodings: identical public halves. + const exported = (key: KeyObject) => + createPublicKey(key).export({ type: "spki", format: "der" }).toString("base64"); + expect(exported(fromDer)).toBe(exported(material.privateKey)); + }); + + it("returns null for empty or unusable input", () => { + expect(loadEd25519PrivateKey("")).toBeNull(); + expect(loadEd25519PrivateKey("definitely-not-a-key")).toBeNull(); + }); + + it("fails fast on an unusable configured signing key", () => { + expect(() => sep10Keys(server.secret(), "not-a-valid-key!!!")).toThrow(/SEP10_JWT_SIGNING_KEY/); + }); + + it("generates ephemeral keys when nothing is configured (dev only)", () => { + const first = sep10Keys("", ""); + expect(first.ephemeral).toBe(true); + resetSep10Keys(); + const second = sep10Keys("", ""); + expect(second.ephemeral).toBe(true); + const exportOne = first.jwtPublicKey.export({ type: "spki", format: "der" }).toString("base64"); + const exportTwo = second.jwtPublicKey.export({ type: "spki", format: "der" }).toString("base64"); + expect(exportOne).not.toBe(exportTwo); + }); +}); diff --git a/src/auth/sep10/sep10.service.ts b/src/auth/sep10/sep10.service.ts new file mode 100644 index 00000000..8feb0ad6 --- /dev/null +++ b/src/auth/sep10/sep10.service.ts @@ -0,0 +1,201 @@ +import { BadRequestException, Inject, Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { StrKey, Transaction, WebAuth } from "@stellar/stellar-sdk"; +import { NETWORK_PASSPHRASES, type AppConfig } from "../../config/configuration"; +import { signEddsaJwt } from "../../common/jwt"; +import { SolversService } from "../../solvers/solvers.service"; +import { SEP10_ACCOUNT_INFO_LOADER, type Sep10AccountInfo, type Sep10AccountInfoLoader } from "./account-info"; +import { SEP10_KEYS, type Sep10KeyMaterial } from "./sep10-keys"; +import { SEP10_NONCE_STORE, type Sep10NonceStore } from "./nonce.store"; + +/** SEP-10 challenge validity window (the protocol's default timeout). */ +export const SEP10_CHALLENGE_TIMEOUT_SECONDS = 300; + +/** + * Session JWT lifetime: 15 minutes per issue #442. Refreshing means + * re-running the challenge flow — there is no refresh token. + */ +export const SEP10_JWT_TTL_SECONDS = 900; + +/** + * Nonce retention: the challenge window plus the 300s clock-drift grace + * `readChallengeTx` applies to `maxTime`, so a challenge issued at `t` stays + * exchangeable until `t + 600` but only once. + */ +export const SEP10_NONCE_TTL_SECONDS = SEP10_CHALLENGE_TIMEOUT_SECONDS * 2; + +/** Claims carried by the session JWT (issue #442). */ +export type Sep10Role = "solver" | "user" | "admin"; + +/** `GET /api/v1/auth/challenge` response — SEP-10 wire shape. */ +export interface Sep10ChallengeResponse { + transaction: string; + network_passphrase: string; +} + +/** `POST /api/v1/auth/token` response: the short-lived session JWT. */ +export interface Sep10TokenResponse { + token: string; + token_type: "Bearer"; + expires_in: number; +} + +/** + * SEP-10 challenge/verify core (issue #442). + * + * `buildChallenge` issues a server-signed challenge transaction (home-domain + * and `web_auth_domain` manage-data operations, 48-byte nonce, 5-minute time + * bounds) and remembers its nonce; `exchange` verifies a client-signed + * challenge against the on-chain account (server signature, time bounds, + * home domain, client signature weight ≥ threshold), atomically consumes the + * nonce — so a captured challenge can never yield a second token — and issues + * a short-lived EdDSA JWT with `sub` and `role`. + */ +@Injectable() +export class Sep10Service { + constructor( + private readonly config: ConfigService, + @Inject(SEP10_NONCE_STORE) private readonly nonces: Sep10NonceStore, + private readonly solvers: SolversService, + @Inject(SEP10_KEYS) private readonly keys: Sep10KeyMaterial, + @Inject(SEP10_ACCOUNT_INFO_LOADER) private readonly accountInfo: Sep10AccountInfoLoader, + ) {} + + /** + * Issues a SEP-10 challenge transaction for `account`. + * + * The challenge is signed by the server keypair, carries the account as + * source of the first `manageData` operation (the nonce), and is bound to + * this deployment's home domain. The nonce is stored immediately so it can + * only ever be exchanged once. + */ + async buildChallenge(account: string): Promise { + if (!StrKey.isValidEd25519PublicKey(account)) { + throw new BadRequestException("account must be a Stellar G-address"); + } + const transaction = WebAuth.buildChallengeTx( + this.keys.challenge, + account, + this.homeDomain, + SEP10_CHALLENGE_TIMEOUT_SECONDS, + this.networkPassphrase, + this.homeDomain, + ); + const nonce = challengeNonce(new Transaction(transaction, this.networkPassphrase)); + await this.nonces.remember(nonce, SEP10_NONCE_TTL_SECONDS); + return { transaction, network_passphrase: this.networkPassphrase }; + } + + /** + * Verifies a client-signed challenge and issues the session JWT. + * + * Verification order: the server half (signature, sequence, time bounds, + * home domain, `web_auth_domain`, nonce shape) via the SDK, then the client + * account's signer weight against its on-chain threshold (multisig-aware), + * then the single-use nonce. Signatures are checked *before* the nonce is + * consumed so an attacker cannot burn a legitimate challenge by replaying a + * malformed exchange. + */ + async exchange(transaction: string): Promise { + const read = this.readChallenge(transaction); + const info = await this.loadAccountInfo(read.clientAccountID); + try { + WebAuth.verifyChallengeTxThreshold( + transaction, + this.keys.challenge.publicKey(), + this.networkPassphrase, + info.threshold, + info.signers, + this.homeDomain, + this.homeDomain, + ); + } catch (error) { + throw challengeRejected(error); + } + + const consumed = await this.nonces.consume(challengeNonce(read.tx)); + if (!consumed) { + throw new BadRequestException( + "SEP-10 challenge rejected: nonce was not issued by this server or has already been used (replay)", + ); + } + + const role = await this.resolveRole(read.clientAccountID); + const issuedAt = Math.floor(Date.now() / 1000); + const token = signEddsaJwt( + { sub: read.clientAccountID, role, iat: issuedAt, exp: issuedAt + SEP10_JWT_TTL_SECONDS }, + this.keys.jwtPrivateKey, + ); + return { token, token_type: "Bearer", expires_in: SEP10_JWT_TTL_SECONDS }; + } + + /** Runs the SDK's challenge reader, mapping every failure to a 400. */ + private readChallenge(transaction: string): ReturnType { + try { + return WebAuth.readChallengeTx( + transaction, + this.keys.challenge.publicKey(), + this.networkPassphrase, + this.homeDomain, + this.homeDomain, + ); + } catch (error) { + throw challengeRejected(error); + } + } + + /** Loads the client account's signers/threshold; unknown accounts are a 400. */ + private async loadAccountInfo(account: string): Promise { + try { + return await this.accountInfo.load(account); + } catch (error) { + const status = (error as { response?: { status?: number }; name?: string })?.response?.status; + if (status === 404 || (error as { name?: string })?.name === "NotFoundError") { + throw new BadRequestException( + "SEP-10 challenge rejected: client account does not exist on the network", + ); + } + throw error; + } + } + + /** + * Derives the `role` claim: `admin` when the account is listed in + * `SEP10_ADMIN_ACCOUNTS`, `solver` when the account is registered with the + * protocol, otherwise `user`. + */ + private async resolveRole(account: string): Promise { + const admins = this.config + .get("sep10AdminAccounts", { infer: true }) + .split(",") + .map((entry) => entry.trim()) + .filter((entry) => entry.length > 0); + if (admins.includes(account)) return "admin"; + const solver = await this.solvers.get(account); + return solver ? "solver" : "user"; + } + + /** The home domain; also used as the challenge's `web_auth_domain` value. */ + private get homeDomain(): string { + return this.config.get("sep10HomeDomain", { infer: true }); + } + + private get networkPassphrase(): string { + return NETWORK_PASSPHRASES[this.config.get("stellar.network", { infer: true })]; + } +} + +/** Extracts the 48-byte base64 nonce from a challenge's first manageData op. */ +function challengeNonce(tx: Transaction): string { + const op = tx.operations[0]; + if (!op || op.type !== "manageData" || op.value == null) { + throw new BadRequestException("SEP-10 challenge rejected: missing nonce manageData operation"); + } + return typeof op.value === "string" ? op.value : Buffer.from(op.value).toString("utf8"); +} + +/** Maps any SDK challenge-verification failure onto a 400 with its reason. */ +function challengeRejected(error: unknown): BadRequestException { + const reason = error instanceof Error ? error.message : String(error); + return new BadRequestException(`SEP-10 challenge rejected: ${reason}`); +} diff --git a/src/auth/solver-credentials/solver-jwt.guard.spec.ts b/src/auth/solver-credentials/solver-jwt.guard.spec.ts new file mode 100644 index 00000000..72abeed8 --- /dev/null +++ b/src/auth/solver-credentials/solver-jwt.guard.spec.ts @@ -0,0 +1,124 @@ +/** + * SolverJwtGuard tests (issue #443/#442): Bearer extraction, EdDSA session + * tokens issued by the SEP-10 flow, HS256 backward compatibility, and the + * subject/path-address match. + */ +import { ForbiddenException, UnauthorizedException, type ExecutionContext } from "@nestjs/common"; +import type { ConfigService } from "@nestjs/config"; +import { Keypair } from "@stellar/stellar-sdk"; +import { generateKeyPairSync } from "node:crypto"; +import type { AppConfig } from "../../config/configuration"; +import { signEddsaJwt, signHs256Jwt } from "../../common/jwt"; +import { resetSep10Keys } from "../sep10/sep10-keys"; +import { SolverJwtGuard } from "./solver-jwt.guard"; + +const HS_SECRET = "hs256-secret-that-is-long-enough-for-tests"; +const solver = Keypair.random(); +const jwtMaterial = generateKeyPairSync("ed25519"); +const jwtPem = jwtMaterial.privateKey.export({ type: "pkcs8", format: "pem" }) as string; + +function fakeConfig(overrides: Record = {}): ConfigService { + const values: Record = { + authJwtSecret: HS_SECRET, + "stellar.signerSecretKey": "", + sep10JwtSigningKey: "", + ...overrides, + }; + return { get: (key: string) => values[key] } as unknown as ConfigService; +} + +/** Builds an ExecutionContext whose getRequest always returns the same object. */ +function makeContext(headers: Record, address: string) { + const request: { headers: Record; params: { address: string }; solverAddress?: string } = + { headers, params: { address } }; + const context = { + switchToHttp: () => ({ getRequest: () => request }), + } as unknown as ExecutionContext; + return { context, request }; +} + +function guardWith(overrides: Record): SolverJwtGuard { + return new SolverJwtGuard(fakeConfig(overrides)); +} + +beforeEach(() => resetSep10Keys()); + +describe("SolverJwtGuard (SEP-10 EdDSA tokens, issue #442)", () => { + it("accepts a valid EdDSA token and attaches the solver address", () => { + const guard = guardWith({ sep10JwtSigningKey: jwtPem }); + const token = signEddsaJwt( + { sub: solver.publicKey(), role: "solver", exp: Math.floor(Date.now() / 1000) + 600 }, + jwtMaterial.privateKey, + ); + const { context, request } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(guard.canActivate(context)).toBe(true); + expect(request.solverAddress).toBe(solver.publicKey()); + }); + + it("accepts EdDSA tokens when the legacy HS256 secret is unset", () => { + const guard = guardWith({ sep10JwtSigningKey: jwtPem, authJwtSecret: "" }); + const token = signEddsaJwt( + { sub: solver.publicKey(), role: "solver", exp: Math.floor(Date.now() / 1000) + 600 }, + jwtMaterial.privateKey, + ); + const { context } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(guard.canActivate(context)).toBe(true); + }); + + it("rejects an expired EdDSA token", () => { + const guard = guardWith({ sep10JwtSigningKey: jwtPem }); + const token = signEddsaJwt({ sub: solver.publicKey(), role: "solver", exp: 1 }, jwtMaterial.privateKey); + const { context } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(UnauthorizedException); + }); + + it("rejects an EdDSA token signed with a foreign key", () => { + const guard = guardWith({ sep10JwtSigningKey: jwtPem }); + const foreign = generateKeyPairSync("ed25519"); + const token = signEddsaJwt( + { sub: solver.publicKey(), role: "solver", exp: Math.floor(Date.now() / 1000) + 600 }, + foreign.privateKey, + ); + const { context } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(UnauthorizedException); + }); +}); + +describe("SolverJwtGuard (legacy HS256 fallback)", () => { + it("still accepts HS256 tokens signed with AUTH_JWT_SECRET", () => { + const guard = guardWith({}); + const token = signHs256Jwt({ sub: solver.publicKey(), exp: Math.floor(Date.now() / 1000) + 600 }, HS_SECRET); + const { context, request } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(guard.canActivate(context)).toBe(true); + expect(request.solverAddress).toBe(solver.publicKey()); + }); + + it("rejects HS256 tokens signed with the wrong secret", () => { + const guard = guardWith({}); + const token = signHs256Jwt({ sub: solver.publicKey(), exp: Math.floor(Date.now() / 1000) + 600 }, "wrong-secret"); + const { context } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(UnauthorizedException); + }); +}); + +describe("SolverJwtGuard (token extraction and subject match)", () => { + it("rejects requests without a Bearer token", () => { + const guard = guardWith({}); + const { context } = makeContext({}, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(UnauthorizedException); + }); + + it("rejects garbage tokens", () => { + const guard = guardWith({}); + const { context } = makeContext({ authorization: "Bearer not.a.jwt" }, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(UnauthorizedException); + }); + + it("rejects tokens whose sub does not match the requested address", () => { + const guard = guardWith({}); + const other = Keypair.random(); + const token = signHs256Jwt({ sub: other.publicKey(), exp: Math.floor(Date.now() / 1000) + 600 }, HS_SECRET); + const { context } = makeContext({ authorization: `Bearer ${token}` }, solver.publicKey()); + expect(() => guard.canActivate(context)).toThrow(ForbiddenException); + }); +}); diff --git a/src/auth/solver-credentials/solver-jwt.guard.ts b/src/auth/solver-credentials/solver-jwt.guard.ts index 78fcffff..138c23af 100644 --- a/src/auth/solver-credentials/solver-jwt.guard.ts +++ b/src/auth/solver-credentials/solver-jwt.guard.ts @@ -7,27 +7,36 @@ import { } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import type { Request } from "express"; +import type { KeyObject } from "node:crypto"; import { AppConfig } from "../../config/configuration"; -import { verifyHs256Jwt } from "../../common/jwt"; +import { verifyEddsaJwt, verifyHs256Jwt } from "../../common/jwt"; +import { sep10JwtPublicKey } from "../sep10/sep10-keys"; type JwtRequest = Request & { solverAddress?: string }; /** * Authenticates a solver-scoped credential request via a SEP-10 JWT - * (issue #443). + * (issue #443/#442). * - * The JWT is read from `Authorization: Bearer`, verified with the shared - * HS256 secret, and its `sub` must match the `:address` path parameter — a - * solver may only mint credentials for its own on-chain identity. The - * verified address is attached to the request for the handler. + * The JWT is read from `Authorization: Bearer`: the 15-minute EdDSA token + * issued by `POST /api/v1/auth/token` is verified first (`SEP10_JWT_SIGNING_KEY`), + * falling back to the legacy HS256 token (`AUTH_JWT_SECRET`) so existing + * deployments keep working. Its `sub` must match the `:address` path + * parameter — a solver may only mint credentials for its own on-chain + * identity. The verified address is attached to the request for the handler. */ @Injectable() export class SolverJwtGuard implements CanActivate { constructor(config: ConfigService) { this.jwtSecret = config.get("authJwtSecret", { infer: true }); + this.sep10PublicKey = sep10JwtPublicKey( + config.get("stellar.signerSecretKey", { infer: true }), + config.get("sep10JwtSigningKey", { infer: true }), + ); } private readonly jwtSecret: string; + private readonly sep10PublicKey: KeyObject | null; canActivate(context: ExecutionContext): boolean { const req = context.switchToHttp().getRequest(); @@ -36,7 +45,9 @@ export class SolverJwtGuard implements CanActivate { if (!presented) { throw new UnauthorizedException("A valid SEP-10 JWT is required"); } - const claims = verifyHs256Jwt(presented, this.jwtSecret); + const claims = + (this.sep10PublicKey ? verifyEddsaJwt(presented, this.sep10PublicKey) : null) ?? + verifyHs256Jwt(presented, this.jwtSecret); if (!claims) { throw new UnauthorizedException("Invalid or expired SEP-10 JWT"); } diff --git a/src/common/jwt.ts b/src/common/jwt.ts index 9558be56..5f0b9347 100644 --- a/src/common/jwt.ts +++ b/src/common/jwt.ts @@ -1,4 +1,12 @@ -import { createHmac, timingSafeEqual } from "node:crypto"; +import { + createHmac, + createPrivateKey, + createPublicKey, + sign as cryptoSign, + timingSafeEqual, + verify as cryptoVerify, + type KeyObject, +} from "node:crypto"; export interface JwtClaims { sub: string; @@ -42,3 +50,82 @@ export function signHs256Jwt(claims: JwtClaims, secret: string): string { const sig = b64url(createHmac("sha256", secret).update(`${header}.${payload}`).digest()); return `${header}.${payload}.${sig}`; } + +/** + * Loads an Ed25519 private key for the SEP-10 session JWTs (issue #442). + * + * Accepts a PKCS#8 PEM (`-----BEGIN PRIVATE KEY-----`), a PEM whose newlines + * were flattened to literal `\n` sequences (common in `.env` files and + * container manifests), or the bare base64 DER body. Returns null when the + * input is empty or is not a usable Ed25519 private key. + */ +export function loadEd25519PrivateKey(secret: string): KeyObject | null { + const normalized = secret.trim().replace(/\\n/g, "\n"); + if (!normalized) return null; + try { + if (normalized.includes("PRIVATE KEY")) { + return createPrivateKey(normalized); + } + // Bare base64 DER (PKCS#8) — wrap it in PEM headers for Node. + const der = Buffer.from(normalized.replace(/\s+/g, ""), "base64"); + if (der.length === 0) return null; + const pem = `-----BEGIN PRIVATE KEY-----\n${der.toString("base64").replace(/(.{64})/g, "$1\n")}\n-----END PRIVATE KEY-----\n`; + return createPrivateKey(pem); + } catch { + return null; + } +} + +/** + * Derives the public verification key for an Ed25519 private key (issue #442). + * Node derives the public key from any private key object, so the public half + * of `SEP10_JWT_SIGNING_KEY` never needs its own configuration. + */ +export function ed25519PublicKeyFor(privateKey: KeyObject): KeyObject { + return createPublicKey(privateKey); +} + +/** + * Signs claims as an `alg: EdDSA` JWT with an Ed25519 private key — the token + * issued by the SEP-10 solver auth flow (issue #442). + */ +export function signEddsaJwt(claims: JwtClaims, privateKey: KeyObject): string { + const header = b64url(Buffer.from(JSON.stringify({ alg: "EdDSA", typ: "JWT" }))); + const payload = b64url(Buffer.from(JSON.stringify(claims))); + const signature = b64url(cryptoSign(null, Buffer.from(`${header}.${payload}`), privateKey)); + return `${header}.${payload}.${signature}`; +} + +/** + * Verifies an EdDSA JWT (the short-lived session token issued by the SEP-10 + * flow, issue #442) and returns its claims, or null when the signature, + * algorithm, `exp`/`nbf` or `sub` is invalid. Tokens signed with any other + * algorithm are rejected, so an HS256 token can never satisfy this check. + */ +export function verifyEddsaJwt( + token: string, + publicKey: KeyObject, + nowSec = Math.floor(Date.now() / 1000), +): JwtClaims | null { + const parts = token.split("."); + if (parts.length !== 3) return null; + const [header, payload, signature] = parts; + try { + const head = JSON.parse(Buffer.from(header, "base64url").toString("utf8")); + if (head.alg !== "EdDSA") return null; + const valid = cryptoVerify( + null, + Buffer.from(`${header}.${payload}`), + publicKey, + Buffer.from(signature, "base64url"), + ); + if (!valid) return null; + const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as JwtClaims; + if (typeof claims.sub !== "string" || !claims.sub) return null; + if (typeof claims.exp === "number" && claims.exp <= nowSec) return null; + if (typeof claims.nbf === "number" && claims.nbf > nowSec) return null; + return claims; + } catch { + return null; + } +} diff --git a/src/config/configuration.ts b/src/config/configuration.ts index f8b52df5..d3d3f961 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -302,6 +302,14 @@ export interface AppConfig { }; /** HS256 secret for solver JWTs (SEP-10 auth, #442); empty disables JWT auth. */ authJwtSecret: string; + /** SEP-10 challenge home domain; also used as the web_auth_domain value (#442). */ + sep10HomeDomain: string; + /** Ed25519 PKCS#8 key signing SEP-10 session JWTs (#442); distinct from the Soroban signer. */ + sep10JwtSigningKey: string; + /** Comma-separated G-addresses granted role=admin in issued JWTs (#442). */ + sep10AdminAccounts: string; + /** Backing store for single-use SEP-10 challenge nonces (#442). */ + sep10NonceStore: "memory" | "redis"; /** * How often (ms) the local rate-limiter fallback prunes expired window * entries (issue #441). Only relevant during a Redis outage. @@ -482,6 +490,11 @@ export default (): AppConfig => ({ drainTimeoutMs: parseInt(process.env.WS_DRAIN_TIMEOUT_MS ?? "25000", 10), }, authJwtSecret: process.env.AUTH_JWT_SECRET ?? "", + // ── SEP-10 solver authentication (issue #442) ───────────────────────────── + sep10HomeDomain: process.env.SEP10_HOME_DOMAIN ?? "localhost", + sep10JwtSigningKey: process.env.SEP10_JWT_SIGNING_KEY ?? "", + sep10AdminAccounts: process.env.SEP10_ADMIN_ACCOUNTS ?? "", + sep10NonceStore: (process.env.SEP10_NONCE_STORE ?? "memory") as "memory" | "redis", rateLimitLocalPruneMs: parseInt(process.env.RATE_LIMIT_LOCAL_PRUNE_MS ?? "60000", 10), // Redis URL for the distributed rate limiter. Defaults to REDIS_URL so an // existing multi-replica deployment keeps a global quota; set it explicitly diff --git a/src/config/env.validation.ts b/src/config/env.validation.ts index c16266dd..c9c1e6ce 100644 --- a/src/config/env.validation.ts +++ b/src/config/env.validation.ts @@ -452,6 +452,25 @@ export const envValidationSchema = Joi.object({ // JWT auth; signature auth keeps working. AUTH_JWT_SECRET: Joi.string().allow("").min(32).default(""), + // ── SEP-10 solver authentication (issue #442) ────────────────────────────── + // Home domain embedded in the challenge (` auth` manage-data key). + // The same value is used for the challenge's `web_auth_domain` operation. + SEP10_HOME_DOMAIN: Joi.string().min(1).default("localhost"), + // Ed25519 PKCS#8 private key signing the short-lived session JWTs (distinct + // from the Soroban signer key). Required in production: an ephemeral dev key + // cannot verify tokens on another replica or after a restart. + SEP10_JWT_SIGNING_KEY: Joi.when("NODE_ENV", { + is: "production", + then: Joi.string().required(), + otherwise: Joi.string().allow("").default(""), + }), + // Comma-separated G-addresses issued role=admin. Empty = nobody. + SEP10_ADMIN_ACCOUNTS: Joi.string().allow("").default(""), + // Backing store for single-use challenge nonces. Use `redis` in any + // multi-replica deployment — with `memory` a captured challenge can be + // replayed against a replica that has not seen it yet. + SEP10_NONCE_STORE: Joi.string().valid("memory", "redis").default("memory"), + // ── Distributed rate limiting (issue #441) ───────────────────────────────── // How often the local rate-limiter fallback prunes expired window entries. // Only relevant during a Redis outage; keeps the fallback map bounded. diff --git a/src/intents/intents.gateway.ts b/src/intents/intents.gateway.ts index 7d2a7b6f..c1243d5a 100644 --- a/src/intents/intents.gateway.ts +++ b/src/intents/intents.gateway.ts @@ -15,7 +15,9 @@ import { WS_MAX_SUBSCRIPTIONS_PER_CONNECTION, } from "../config/limits.config"; import configuration, { AppConfig } from "../config/configuration"; -import { verifyHs256Jwt } from "../common/jwt"; +import { sep10JwtPublicKey } from "../auth/sep10/sep10-keys"; +import { verifyEddsaJwt, verifyHs256Jwt } from "../common/jwt"; +import type { KeyObject } from "node:crypto"; import { Backplane, SequencedEvent, WS_BACKPLANE } from "./backplane/backplane.types"; import { MemoryBackplane } from "./backplane/memory.backplane"; import { ConnectionState, resolveClientIp, EncodingFormat } from "./ws/connection-state"; @@ -58,6 +60,8 @@ export class IntentsGateway private heartbeatTimer: any; private readonly wsConfig: AppConfig["ws"]; private readonly jwtSecret: string; + /** Public half of `SEP10_JWT_SIGNING_KEY` — verifies SEP-10 session JWTs (#442). */ + private readonly sep10PublicKey: KeyObject | null; /** Fan-out + global sequencing (issue #454): memory or Redis Streams. */ private readonly backplane: Backplane; @@ -94,6 +98,10 @@ export class IntentsGateway const defaults = configuration(); this.wsConfig = config?.get("ws", { infer: true }) ?? defaults.ws; this.jwtSecret = config?.get("authJwtSecret", { infer: true }) ?? defaults.authJwtSecret; + this.sep10PublicKey = sep10JwtPublicKey( + config?.get("stellar.signerSecretKey", { infer: true }) ?? defaults.stellar.signerSecretKey, + config?.get("sep10JwtSigningKey", { infer: true }) ?? defaults.sep10JwtSigningKey, + ); this.heartbeatIntervalMs = resolveHeartbeatIntervalMs(); this.heartbeatTimer = setInterval(() => this.heartbeat(), this.heartbeatIntervalMs); this.backplane = this.createBackplane(); @@ -663,10 +671,16 @@ export class IntentsGateway } /** - * Authenticates with a solver JWT from the SEP-10 flow (issue #455 / #442). + * Authenticates with a session JWT from the SEP-10 flow (issue #442/#455). + * + * EdDSA tokens issued by `POST /api/v1/auth/token` are tried first; the + * legacy HS256 token (`AUTH_JWT_SECRET`) remains accepted so existing + * deployments keep working. */ private async authenticateJwt(client: WebSocket, token: string): Promise { - const claims = verifyHs256Jwt(token, this.jwtSecret); + const claims = + (this.sep10PublicKey ? verifyEddsaJwt(token, this.sep10PublicKey) : null) ?? + verifyHs256Jwt(token, this.jwtSecret); if (!claims) { this.send(client, JSON.stringify({ type: "auth_error", reason: "invalid or expired token" })); return; diff --git a/test/__mocks__/@stellar/stellar-sdk.ts b/test/__mocks__/@stellar/stellar-sdk.ts index 68c2007a..033b05be 100644 --- a/test/__mocks__/@stellar/stellar-sdk.ts +++ b/test/__mocks__/@stellar/stellar-sdk.ts @@ -106,6 +106,9 @@ export const BASE_FEE = real.BASE_FEE; // `new StellarSdk.Horizon.Server(...)`). Without these the mock leaves // `StellarSdk.Horizon` undefined and every e2e suite that boots the app fails. export const Horizon = real.Horizon; +// SEP-10 challenge builders/verifiers (issue #442) — genuine SDK, only +// SorobanRpc is stubbed above. +export const WebAuth = real.WebAuth; export const Utils = real.Utils; export const Config = real.Config; export const MuxedAccount = real.MuxedAccount;