ソースを参照

feat: scaffold domestic voc backend

gangvy 2 ヶ月 前
コミット
7a079a082f

+ 17 - 0
.env.example

@@ -0,0 +1,17 @@
+NODE_ENV=development
+HOST=127.0.0.1
+PORT=4400
+DATABASE_URL=
+MIGRATION_DATABASE_URL=
+DATABASE_POOL_MAX=10
+DATABASE_IDLE_TIMEOUT_MS=30000
+DATABASE_STATEMENT_TIMEOUT_MS=30000
+PARSE_APP_ID=
+PARSE_MASTER_KEY=
+PARSE_MAINTENANCE_KEY=
+PARSE_SERVER_URL=http://127.0.0.1:4400/parse
+FMODE_BASE_URL=http://127.0.0.1:3000/api/voc-e-commerce
+FMODE_API_KEY=
+FMODE_TIMEOUT_MS=30000
+FMODE_RETRIES=2
+CORS_ORIGINS=http://127.0.0.1:4300,http://localhost:4200

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+node_modules/
+dist/
+.env
+.env.*
+!.env.example
+coverage/
+logs/
+*.log
+.DS_Store
+.idea/
+.vscode/

+ 2 - 0
.nvmrc

@@ -0,0 +1,2 @@
+22.13.0
+

+ 131 - 0
README.md

@@ -0,0 +1,131 @@
+# SaaS VOC Server
+
+Independent backend template for the domestic ecommerce VOC product. The first case workspace is Demashi on JD. This repository does not connect to the cross-border production database and does not contain any Fmode, JustOneAPI, Parse, or PostgreSQL credential.
+
+## Current state
+
+Completed on 2026-07-23:
+
+- Independent Git repository and Node.js 22 / TypeScript service baseline.
+- Independent PostgreSQL schema with workspace, source, product, metric, relation, review, import, and sync-job tables.
+- Independent Parse Server application configuration on the same dedicated database.
+- Strict startup validation for every required credential.
+- `GET /health` database readiness check.
+- `POST /api/domestic-voc/sync` idempotent manual-sync queue entry.
+- `GET /api/domestic-voc/jobs/:id` sync-job status.
+- `GET /api/domestic-voc/snapshot` response compatible with the frontend `DomesticDataset` interface.
+- Server-side Fmode gateway client with timeout, retry, and credential redaction behavior.
+- Deterministic identities for products, relations, reviews, and daily sync requests.
+- Bounded batch importer for the normalized Demashi dataset.
+
+The current case import resolves to 2,817 operating products, 9,717 daily metrics, 40 relations, and 38 internal `relation_stub` records required to preserve foreign keys. Stub records are excluded from the frontend product list and are never presented as collected product facts.
+
+The worker does not call a JD upstream endpoint yet. A sync request is queued truthfully as `pending`; it is never marked complete until the real JD detail and review contracts are verified.
+
+## Architecture
+
+```text
+Saas-voc frontend
+  -> saas-voc-server
+      -> dedicated Parse application
+      -> dedicated PostgreSQL database (voc schema)
+      -> sync queue
+          -> existing /api/voc-e-commerce gateway
+              -> JustOneAPI
+```
+
+The browser only calls this service. `FMODE_API_KEY` is used in the server-side `Authorization` header and is never returned to the browser or included in a request URL.
+
+## Runtime
+
+Use Node.js 22.13 or newer within the Node 22 release line. The repository intentionally pins Node 22 because Parse Server publishes explicit supported runtime ranges.
+
+Required configuration is listed in `.env.example`. Empty secrets are rejected before the HTTP server starts. Generate independent random values for `PARSE_MASTER_KEY` and `PARSE_MAINTENANCE_KEY`; do not reuse any value from `moshengqi-server` or `future-server`. `DATABASE_URL` is the least-privilege runtime connection. `MIGRATION_DATABASE_URL` is the schema-owner connection used only by `npm run migrate`; it falls back to `DATABASE_URL` for local development.
+
+Local database:
+
+```powershell
+$env:POSTGRES_PASSWORD = '<local-only-password>'
+docker compose up -d postgres
+```
+
+The compose database listens only on `127.0.0.1:54329`. A local `DATABASE_URL` therefore uses database `saas_voc` on port `54329`.
+
+Install, migrate, and run:
+
+```powershell
+npm ci
+npm run migrate
+npm run dev
+```
+
+Import the current normalized case after migration:
+
+```powershell
+npm run import:dataset -- "E:\workspace\Saas-voc\src\assets\data\demashi-summary.json"
+```
+
+Default local API address:
+
+```text
+http://127.0.0.1:4400
+```
+
+## API
+
+Queue a JD product and review sync:
+
+```http
+POST /api/domestic-voc/sync
+Idempotency-Key: optional-client-key
+Content-Type: application/json
+
+{
+  "workspaceId": "demashi",
+  "platform": "jd",
+  "productIds": ["11266507445"],
+  "scopes": ["product", "reviews"]
+}
+```
+
+When `Idempotency-Key` is absent, the service derives a stable key from workspace, platform, products, scopes, and UTC date. The same daily request returns the same job.
+
+Read status and frontend data:
+
+```text
+GET /api/domestic-voc/jobs/{jobId}
+GET /api/domestic-voc/snapshot?workspaceId=demashi&platform=jd
+```
+
+An empty database returns a valid empty dataset. It does not invent reviews, ratings, sentiment, pain points, or AI output.
+
+## Database rules
+
+- Runtime identities are enforced by composite unique constraints, not by check-then-insert logic.
+- Import and sync writers must use `INSERT ... ON CONFLICT` in batches.
+- Foreign-key columns and common workspace/date filters are indexed.
+- Workers claim jobs with `FOR UPDATE SKIP LOCKED`.
+- External HTTP requests must run outside database transactions.
+- Production deployments must use a pooled, least-privilege runtime role. Migration credentials should be separate from runtime credentials.
+- The HTTP server never runs DDL automatically; migrations are an explicit deployment step.
+
+## Security status
+
+`parse-server@9.10.0` replaces the initially evaluated v8 line, removing all high and critical audit findings. A `ws@8.21.0` override is retained because Parse currently pins an older vulnerable patch. The remaining audit findings are moderate transitive dependencies in Parse push/Firebase and redirect support; push and LiveQuery are not configured in this template. Re-run `npm audit --omit=dev` before each deployment and do not use `npm audit fix --force`, which currently proposes an unsafe Parse downgrade.
+
+## Verification
+
+```powershell
+npm run build
+npm test
+npm audit --omit=dev
+```
+
+Current result:
+
+- TypeScript build: passed.
+- Unit and HTTP contract tests: 13 passed.
+- Production dependency audit: 0 critical, 0 high, 13 moderate.
+- Live JD contract and quota-consuming request: intentionally not run yet.
+
+See `TASKS.md` for the implementation sequence and acceptance boundary.

+ 55 - 0
TASKS.md

@@ -0,0 +1,55 @@
+# Backend implementation tasks
+
+Status date: 2026-07-23
+
+## Phase 1 - isolated backend baseline
+
+- [x] Create `E:\workspace\server\saas-voc-server` as an independent Git repository.
+- [x] Keep `moshengqi-server` and `future-server` read-only.
+- [x] Add strict environment validation without credential fallbacks.
+- [x] Add pooled PostgreSQL access and an idempotent migration runner.
+- [x] Add an independent Parse Server application configuration.
+- [x] Add the initial domestic VOC schema, constraints, foreign keys, and indexes.
+- [x] Seed the `demashi` workspace and JD source metadata.
+- [x] Add health, sync, job-status, and snapshot endpoints.
+- [x] Add Fmode timeout/retry/authentication behavior behind a server-only client.
+- [x] Add unit tests and compile under strict TypeScript settings.
+- [ ] Run migrations against a newly provisioned PostgreSQL database.
+- [ ] Smoke-test the HTTP service under the supported Node 22 runtime.
+
+## Phase 2 - case-data persistence
+
+- [x] Add a batch importer for the normalized Demashi dataset.
+- [ ] UPSERT 2,817 operating products, 38 relation stubs, and 9,717 daily metric rows in bounded batches.
+- [ ] UPSERT 40 competitor relations with deterministic relation keys.
+- [ ] Verify snapshot totals against `demashi-summary.json`.
+- [x] Switch `DomesticDatasetService` between static case mode and backend API mode.
+- [ ] Run the existing 29-route desktop/mobile audit against API mode.
+
+## Phase 3 - real JD source contract
+
+- [ ] Make one quota-controlled JD search request through the existing Fmode gateway.
+- [ ] Confirm the exact JD product-detail path, parameters, response envelope, and error codes.
+- [ ] Confirm the exact JD review path, pagination fields, and any async task/status flow.
+- [ ] Save sanitized response fixtures without credentials or personal data.
+- [ ] Implement JD product and review adapters against those fixtures.
+- [ ] Process queued jobs with partial-failure events and bounded retries.
+- [ ] UPSERT source results and expose truthful progress through the job endpoint.
+
+## Phase 4 - deployable closure
+
+- [ ] Provision a dedicated PostgreSQL database and least-privilege runtime role.
+- [ ] Provision independent Parse application credentials.
+- [ ] Put TLS and same-origin reverse proxying in front of `/parse` and `/api/domestic-voc`.
+- [ ] Add authentication/authorization before enabling the frontend `AuthGuard`.
+- [ ] Add backup, restore, retention, and failed-job replay procedures.
+- [ ] Resolve or formally accept remaining moderate Parse transitive advisories.
+- [ ] Complete end-to-end acceptance with Demashi product detail and real review evidence.
+
+## Explicitly deferred
+
+- Multi-platform adapters beyond JD.
+- Multi-tenant billing and complex role management.
+- Scheduled full-catalog crawling.
+- AI reports, sentiment, pain points, or recommendations without review evidence.
+- Any reuse of the cross-border production database or credentials.

+ 20 - 0
compose.yaml

@@ -0,0 +1,20 @@
+services:
+  postgres:
+    image: postgres:16-alpine
+    environment:
+      POSTGRES_DB: saas_voc
+      POSTGRES_USER: saas_voc_owner
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in the shell}
+    ports:
+      - "127.0.0.1:54329:5432"
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U saas_voc_owner -d saas_voc"]
+      interval: 5s
+      timeout: 3s
+      retries: 10
+    volumes:
+      - saas_voc_postgres:/var/lib/postgresql/data
+
+volumes:
+  saas_voc_postgres:
+

+ 201 - 0
migrations/001_initial_domain.sql

@@ -0,0 +1,201 @@
+CREATE SCHEMA IF NOT EXISTS voc;
+
+CREATE TABLE IF NOT EXISTS voc.workspace (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  public_id text NOT NULL UNIQUE,
+  name text NOT NULL,
+  case_name text NOT NULL,
+  status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS voc.source_connection (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  connection_kind text NOT NULL DEFAULT 'fmode_gateway',
+  status text NOT NULL DEFAULT 'configured' CHECK (status IN ('configured', 'active', 'disabled', 'error')),
+  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
+  last_checked_at timestamptz,
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  UNIQUE (workspace_id, platform, connection_kind)
+);
+
+CREATE INDEX IF NOT EXISTS source_connection_workspace_idx
+  ON voc.source_connection (workspace_id);
+
+CREATE TABLE IF NOT EXISTS voc.import_batch (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  public_id text NOT NULL UNIQUE,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  source_kind text NOT NULL,
+  source_file text,
+  source_hash text,
+  sheet_names jsonb NOT NULL DEFAULT '[]'::jsonb,
+  status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'partial', 'failed')),
+  total_rows integer NOT NULL DEFAULT 0 CHECK (total_rows >= 0),
+  success_rows integer NOT NULL DEFAULT 0 CHECK (success_rows >= 0),
+  failed_rows integer NOT NULL DEFAULT 0 CHECK (failed_rows >= 0),
+  started_at timestamptz,
+  completed_at timestamptz,
+  created_at timestamptz NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS import_batch_workspace_created_idx
+  ON voc.import_batch (workspace_id, created_at DESC);
+
+CREATE TABLE IF NOT EXISTS voc.product (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  product_id text NOT NULL,
+  product_key text NOT NULL,
+  role text NOT NULL DEFAULT 'own' CHECK (role IN ('own', 'competitor')),
+  brand text NOT NULL DEFAULT '',
+  title text NOT NULL DEFAULT '',
+  model text NOT NULL DEFAULT '',
+  category_1 text NOT NULL DEFAULT '',
+  category_2 text NOT NULL DEFAULT '',
+  category_3 text NOT NULL DEFAULT '',
+  source text NOT NULL DEFAULT '',
+  raw_payload jsonb,
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  UNIQUE (workspace_id, platform, product_id),
+  UNIQUE (workspace_id, product_key)
+);
+
+CREATE INDEX IF NOT EXISTS product_workspace_role_idx
+  ON voc.product (workspace_id, role, id);
+CREATE INDEX IF NOT EXISTS product_workspace_category_idx
+  ON voc.product (workspace_id, category_2, category_3);
+
+CREATE TABLE IF NOT EXISTS voc.daily_metric (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  product_id bigint NOT NULL REFERENCES voc.product(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  metric_date date NOT NULL,
+  source text NOT NULL,
+  gmv numeric(18, 2) NOT NULL DEFAULT 0,
+  sold_units integer NOT NULL DEFAULT 0,
+  transaction_orders integer NOT NULL DEFAULT 0,
+  transaction_customers integer NOT NULL DEFAULT 0,
+  impressions bigint NOT NULL DEFAULT 0,
+  clicks bigint NOT NULL DEFAULT 0,
+  views bigint NOT NULL DEFAULT 0,
+  visitors bigint NOT NULL DEFAULT 0,
+  cart_units integer NOT NULL DEFAULT 0,
+  order_amount numeric(18, 2) NOT NULL DEFAULT 0,
+  order_units integer NOT NULL DEFAULT 0,
+  order_count integer NOT NULL DEFAULT 0,
+  refund_amount numeric(18, 2) NOT NULL DEFAULT 0,
+  refund_units integer NOT NULL DEFAULT 0,
+  refund_orders integer NOT NULL DEFAULT 0,
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  UNIQUE (workspace_id, platform, product_id, metric_date, source)
+);
+
+CREATE INDEX IF NOT EXISTS daily_metric_product_date_idx
+  ON voc.daily_metric (product_id, metric_date);
+CREATE INDEX IF NOT EXISTS daily_metric_workspace_date_idx
+  ON voc.daily_metric (workspace_id, metric_date);
+
+CREATE TABLE IF NOT EXISTS voc.product_relation (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  own_product_id bigint NOT NULL REFERENCES voc.product(id) ON DELETE CASCADE,
+  competitor_product_id bigint NOT NULL REFERENCES voc.product(id) ON DELETE CASCADE,
+  relation_key text NOT NULL,
+  category text NOT NULL DEFAULT '',
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  CHECK (own_product_id <> competitor_product_id),
+  UNIQUE (workspace_id, platform, own_product_id, competitor_product_id),
+  UNIQUE (workspace_id, relation_key)
+);
+
+CREATE INDEX IF NOT EXISTS product_relation_own_idx
+  ON voc.product_relation (own_product_id, competitor_product_id);
+CREATE INDEX IF NOT EXISTS product_relation_competitor_idx
+  ON voc.product_relation (competitor_product_id);
+CREATE INDEX IF NOT EXISTS product_relation_workspace_idx
+  ON voc.product_relation (workspace_id);
+
+CREATE TABLE IF NOT EXISTS voc.review (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  product_id bigint NOT NULL REFERENCES voc.product(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  source_review_id text,
+  review_key text NOT NULL,
+  rating numeric(3, 2),
+  content text NOT NULL,
+  review_date timestamptz,
+  raw_payload jsonb,
+  created_at timestamptz NOT NULL DEFAULT now(),
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  CHECK (rating IS NULL OR (rating >= 0 AND rating <= 5)),
+  UNIQUE (workspace_id, platform, review_key)
+);
+
+CREATE INDEX IF NOT EXISTS review_product_date_idx
+  ON voc.review (product_id, review_date DESC, id DESC);
+CREATE INDEX IF NOT EXISTS review_workspace_date_idx
+  ON voc.review (workspace_id, review_date DESC, id DESC);
+
+CREATE TABLE IF NOT EXISTS voc.sync_job (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  public_id text NOT NULL UNIQUE,
+  workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
+  platform text NOT NULL,
+  idempotency_key text NOT NULL,
+  status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'partial', 'failed', 'cancelled')),
+  scopes jsonb NOT NULL DEFAULT '[]'::jsonb,
+  product_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
+  progress integer NOT NULL DEFAULT 0 CHECK (progress >= 0 AND progress <= 100),
+  attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0),
+  max_attempts integer NOT NULL DEFAULT 3 CHECK (max_attempts >= 1),
+  worker_id text,
+  error_summary text,
+  requested_at timestamptz NOT NULL DEFAULT now(),
+  started_at timestamptz,
+  completed_at timestamptz,
+  updated_at timestamptz NOT NULL DEFAULT now(),
+  UNIQUE (workspace_id, idempotency_key)
+);
+
+CREATE INDEX IF NOT EXISTS sync_job_workspace_requested_idx
+  ON voc.sync_job (workspace_id, requested_at DESC);
+CREATE INDEX IF NOT EXISTS sync_job_pending_idx
+  ON voc.sync_job (requested_at, id)
+  WHERE status = 'pending';
+
+CREATE TABLE IF NOT EXISTS voc.sync_job_event (
+  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+  sync_job_id bigint NOT NULL REFERENCES voc.sync_job(id) ON DELETE CASCADE,
+  level text NOT NULL DEFAULT 'info' CHECK (level IN ('debug', 'info', 'warning', 'error')),
+  event_type text NOT NULL,
+  message text NOT NULL,
+  details jsonb NOT NULL DEFAULT '{}'::jsonb,
+  created_at timestamptz NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS sync_job_event_job_created_idx
+  ON voc.sync_job_event (sync_job_id, created_at, id);
+
+INSERT INTO voc.workspace (public_id, name, case_name)
+VALUES ('demashi', 'Demashi JD VOC', 'Demashi')
+ON CONFLICT (public_id) DO NOTHING;
+
+INSERT INTO voc.source_connection (workspace_id, platform, connection_kind, status, metadata)
+SELECT id, 'jd', 'fmode_gateway', 'configured', '{"credentialStorage":"environment"}'::jsonb
+FROM voc.workspace
+WHERE public_id = 'demashi'
+ON CONFLICT (workspace_id, platform, connection_kind) DO NOTHING;
+

+ 6474 - 0
package-lock.json

@@ -0,0 +1,6474 @@
+{
+  "name": "saas-voc-server",
+  "version": "0.1.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "saas-voc-server",
+      "version": "0.1.0",
+      "dependencies": {
+        "cors": "^2.8.6",
+        "dotenv": "^17.2.3",
+        "express": "^5.2.1",
+        "parse-server": "^9.10.0",
+        "pg": "^8.22.0",
+        "zod": "^4.4.3"
+      },
+      "devDependencies": {
+        "@types/cors": "^2.8.19",
+        "@types/express": "^5.0.6",
+        "@types/node": "^24.10.0",
+        "@types/pg": "^8.16.0",
+        "tsx": "^4.23.1",
+        "typescript": "^7.0.2"
+      },
+      "engines": {
+        "node": ">=22.13.0 <23"
+      }
+    },
+    "node_modules/@apollo/cache-control-types": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz",
+      "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==",
+      "license": "MIT",
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/protobufjs": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.8.tgz",
+      "integrity": "sha512-r7xNeUqZX+eBBEmyvaPw0/cSz6zgf5jdH8mjUz8ynKpNs/GU7vi2T7sNcZINk2ZID7wwjG91FCgdpCrQuJ8rzA==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "apollo-pbjs": "bin/pbjs",
+        "apollo-pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@apollo/server": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz",
+      "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/cache-control-types": "^1.0.3",
+        "@apollo/server-gateway-interface": "^2.0.0",
+        "@apollo/usage-reporting-protobuf": "^4.1.1",
+        "@apollo/utils.createhash": "^3.0.0",
+        "@apollo/utils.fetcher": "^3.0.0",
+        "@apollo/utils.isnodelike": "^3.0.0",
+        "@apollo/utils.keyvaluecache": "^4.0.0",
+        "@apollo/utils.logger": "^3.0.0",
+        "@apollo/utils.usagereporting": "^2.1.0",
+        "@apollo/utils.withrequired": "^3.0.0",
+        "@graphql-tools/schema": "^10.0.0",
+        "async-retry": "^1.2.1",
+        "body-parser": "^2.2.2",
+        "content-type": "^1.0.5",
+        "cors": "^2.8.5",
+        "finalhandler": "^2.1.0",
+        "loglevel": "^1.6.8",
+        "lru-cache": "^11.1.0",
+        "negotiator": "^1.0.0",
+        "uuid": "^11.1.0",
+        "whatwg-mimetype": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "graphql": "^16.11.0"
+      }
+    },
+    "node_modules/@apollo/server-gateway-interface": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz",
+      "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/usage-reporting-protobuf": "^4.1.1",
+        "@apollo/utils.fetcher": "^3.0.0",
+        "@apollo/utils.keyvaluecache": "^4.0.0",
+        "@apollo/utils.logger": "^3.0.0"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/usage-reporting-protobuf": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.2.tgz",
+      "integrity": "sha512-aTnAD41RYz0d5dawlyR5Iclkgzx0Xb0njUJmEfvZ6pS4f4HU8wCYyctPpWat/HWp2PmRwDfX5R1k4uVcDKZ4xA==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/protobufjs": "1.2.8"
+      }
+    },
+    "node_modules/@apollo/utils.createhash": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz",
+      "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/utils.isnodelike": "^3.0.0",
+        "sha.js": "^2.4.11"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@apollo/utils.dropunuseddefinitions": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz",
+      "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.fetcher": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz",
+      "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@apollo/utils.isnodelike": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz",
+      "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@apollo/utils.keyvaluecache": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz",
+      "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/utils.logger": "^3.0.0",
+        "lru-cache": "^11.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@apollo/utils.logger": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz",
+      "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@apollo/utils.printwithreducedwhitespace": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz",
+      "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.removealiases": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz",
+      "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.sortast": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz",
+      "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==",
+      "license": "MIT",
+      "dependencies": {
+        "lodash.sortby": "^4.7.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.stripsensitiveliterals": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz",
+      "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.usagereporting": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz",
+      "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@apollo/usage-reporting-protobuf": "^4.1.0",
+        "@apollo/utils.dropunuseddefinitions": "^2.0.1",
+        "@apollo/utils.printwithreducedwhitespace": "^2.0.1",
+        "@apollo/utils.removealiases": "2.0.1",
+        "@apollo/utils.sortast": "^2.0.1",
+        "@apollo/utils.stripsensitiveliterals": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "graphql": "14.x || 15.x || 16.x"
+      }
+    },
+    "node_modules/@apollo/utils.withrequired": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz",
+      "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@as-integrations/express5": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@as-integrations/express5/-/express5-1.1.2.tgz",
+      "integrity": "sha512-BxfwtcWNf2CELDkuPQxi5Zl3WqY/dQVJYafeCBOGoFQjv5M0fjhxmAFZ9vKx/5YKKNeok4UY6PkFbHzmQrdxIA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@apollo/server": "^4.0.0 || ^5.0.0",
+        "express": "^5.0.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.29.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+      "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.29.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz",
+      "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js-pure": "^3.48.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "@so-ric/colorspace": "^1.1.6",
+        "enabled": "2.0.x",
+        "kuler": "^2.0.0"
+      }
+    },
+    "node_modules/@emnapi/core": {
+      "version": "1.11.2",
+      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz",
+      "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/wasi-threads": "1.2.2",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.11.2",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
+      "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/wasi-threads": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+      "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "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,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@fastify/busboy": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+      "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
+      "license": "MIT"
+    },
+    "node_modules/@firebase/app-check-interop-types": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.4.tgz",
+      "integrity": "sha512-zz3i6e13B8BfWiLy8MABtTh8aGIACgKbf9UVnyHcWs+yQzJXgQcl8A46b0zfaiJHdQ+niF0ouAfcpuf+3LMPQg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@firebase/app-types": {
+      "version": "0.9.5",
+      "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.5.tgz",
+      "integrity": "sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@firebase/logger": "0.5.1"
+      }
+    },
+    "node_modules/@firebase/auth-interop-types": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.5.tgz",
+      "integrity": "sha512-1Li/YuBDBAXcKv7BzY4U28gontUmAaw53sYiqbaVOMCFb2lFKK/c3CGMUWqtwe7+TXrl3poWnTCL5umYBg85Eg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@firebase/component": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.3.tgz",
+      "integrity": "sha512-wFofIaa2879ogD/WvkjYXJxRmfnL0scen6ORgaC3na1FNOR9ASIUANQdhqQcmWu/h77/pVHY7ch5flewa5Bcew==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@firebase/util": "1.15.1",
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/@firebase/database": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.3.tgz",
+      "integrity": "sha512-XwWCa+E4TvNGpGwXrycLRNfdogADwFcvuhyow6wDWma9W54roaQIhe+4PM0KiLsIftBdSCGI7OKCXrdSRHbIhw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@firebase/app-check-interop-types": "0.3.4",
+        "@firebase/auth-interop-types": "0.2.5",
+        "@firebase/component": "0.7.3",
+        "@firebase/logger": "0.5.1",
+        "@firebase/util": "1.15.1",
+        "faye-websocket": "0.11.4",
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/@firebase/database-compat": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.4.tgz",
+      "integrity": "sha512-3pK35F1MAgmqFJQlf2nhQl44vtAXQO1uaCaQOEUI9kCRtLFqi7N+QRKR7lFZPg+xIZIyubgxQaxY69YgfZRZWg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@firebase/component": "0.7.3",
+        "@firebase/database": "1.1.3",
+        "@firebase/database-types": "1.0.20",
+        "@firebase/logger": "0.5.1",
+        "@firebase/util": "1.15.1",
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/@firebase/database-types": {
+      "version": "1.0.20",
+      "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.20.tgz",
+      "integrity": "sha512-kegbOk/w8iU64pr0q6k2ItyNGjnQBMHFhwS7ohdWI4W+pc0/zhhdGXTdFj6X1oxItRjPoYOsSQmERgBkn/ihxw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@firebase/app-types": "0.9.5",
+        "@firebase/util": "1.15.1"
+      }
+    },
+    "node_modules/@firebase/logger": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.1.tgz",
+      "integrity": "sha512-vZKLsqE1ABOy8OjQiE7cUTFn4gvaqlk88yp8N94Pk/sDpq61YqZGqmVFZTvOyflTwuYFcWirBdYGoJgbDaXKYQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/@firebase/util": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.1.tgz",
+      "integrity": "sha512-LUdM4Wg7YM9Pq/49nGYySJA0CSQEKnGffFzWV8+6gXN7mGxn+FL1IqvFbuZUtAQcfZgHYDwCE1wwlK7rB7gl2g==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
+    "node_modules/@google-cloud/firestore": {
+      "version": "7.11.6",
+      "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz",
+      "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0",
+        "fast-deep-equal": "^3.1.1",
+        "functional-red-black-tree": "^1.0.1",
+        "google-gax": "^4.3.3",
+        "protobufjs": "^7.2.6"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@google-cloud/paginator": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
+      "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "arrify": "^2.0.0",
+        "extend": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@google-cloud/projectify": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
+      "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@google-cloud/promisify": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
+      "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@google-cloud/storage": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.21.0.tgz",
+      "integrity": "sha512-l+IFTkd+6Y5LoAuXyYCKNAKtw/Ci+rAMqgdTB1jv4iZiLhw0rtq+0qjIRbBizXkNzEFmXiXUW0H7sZQQvk1ffA==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@google-cloud/paginator": "^5.0.0",
+        "@google-cloud/projectify": "^4.0.0",
+        "@google-cloud/promisify": "<4.1.0",
+        "abort-controller": "^3.0.0",
+        "async-retry": "^1.3.3",
+        "duplexify": "^4.1.3",
+        "fast-xml-parser": "^5.3.4",
+        "gaxios": "^6.0.2",
+        "google-auth-library": "^9.6.3",
+        "html-entities": "^2.5.2",
+        "mime": "^3.0.0",
+        "p-limit": "^3.0.1",
+        "retry-request": "^7.0.0",
+        "teeny-request": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@google-cloud/storage/node_modules/gcp-metadata": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+      "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "gaxios": "^6.1.1",
+        "google-logging-utils": "^0.0.2",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@google-cloud/storage/node_modules/google-auth-library": {
+      "version": "9.15.1",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+      "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "gaxios": "^6.1.1",
+        "gcp-metadata": "^6.1.0",
+        "gtoken": "^7.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@google-cloud/storage/node_modules/google-logging-utils": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+      "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@google-cloud/storage/node_modules/mime": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+      "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/@graphql-tools/merge": {
+      "version": "9.1.7",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz",
+      "integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@graphql-tools/utils": "^11.0.0",
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-tools/schema": {
+      "version": "10.0.31",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz",
+      "integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@graphql-tools/merge": "^9.1.7",
+        "@graphql-tools/utils": "^11.0.0",
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-tools/utils": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz",
+      "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==",
+      "license": "MIT",
+      "dependencies": {
+        "@graphql-typed-document-node/core": "^3.1.1",
+        "@whatwg-node/promise-helpers": "^1.0.0",
+        "cross-inspect": "1.0.1",
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-typed-document-node/core": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+      "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.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",
+      "optional": true,
+      "dependencies": {
+        "@grpc/proto-loader": "^0.8.0",
+        "@js-sdsl/ordered-map": "^4.4.2"
+      },
+      "engines": {
+        "node": ">=12.10.0"
+      }
+    },
+    "node_modules/@grpc/grpc-js/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",
+      "optional": true,
+      "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/@grpc/grpc-js/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",
+      "optional": true
+    },
+    "node_modules/@grpc/proto-loader": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+      "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "lodash.camelcase": "^4.3.0",
+        "long": "^5.0.0",
+        "protobufjs": "^7.2.5",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@grpc/proto-loader/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",
+      "optional": true
+    },
+    "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",
+      "optional": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/@ldapjs/asn1": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz",
+      "integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT"
+    },
+    "node_modules/@ldapjs/attribute": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz",
+      "integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "2.0.0",
+        "@ldapjs/protocol": "^1.2.1",
+        "process-warning": "^2.1.0"
+      }
+    },
+    "node_modules/@ldapjs/change": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz",
+      "integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "2.0.0",
+        "@ldapjs/attribute": "1.0.0"
+      }
+    },
+    "node_modules/@ldapjs/controls": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz",
+      "integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "^1.2.0",
+        "@ldapjs/protocol": "^1.2.1"
+      }
+    },
+    "node_modules/@ldapjs/controls/node_modules/@ldapjs/asn1": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz",
+      "integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT"
+    },
+    "node_modules/@ldapjs/dn": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz",
+      "integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "2.0.0",
+        "process-warning": "^2.1.0"
+      }
+    },
+    "node_modules/@ldapjs/filter": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz",
+      "integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "2.0.0",
+        "@ldapjs/protocol": "^1.2.1",
+        "process-warning": "^2.1.0"
+      }
+    },
+    "node_modules/@ldapjs/messages": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz",
+      "integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "^2.0.0",
+        "@ldapjs/attribute": "^1.0.0",
+        "@ldapjs/change": "^1.0.0",
+        "@ldapjs/controls": "^2.1.0",
+        "@ldapjs/dn": "^1.1.0",
+        "@ldapjs/filter": "^2.1.1",
+        "@ldapjs/protocol": "^1.2.1",
+        "process-warning": "^2.2.0"
+      }
+    },
+    "node_modules/@ldapjs/protocol": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz",
+      "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT"
+    },
+    "node_modules/@mongodb-js/saslprep": {
+      "version": "1.4.12",
+      "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.12.tgz",
+      "integrity": "sha512-QAfAMwNgnYxZ2C6D1HgeP7Gc4i/uvJRim415PCIL9ptRxWMNbWeLBYb2/9R4pGKny/s1FVu2JA2cxCUBUOggrA==",
+      "license": "MIT",
+      "dependencies": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
+    "node_modules/@napi-rs/wasm-runtime": {
+      "version": "0.2.12",
+      "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+      "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/core": "^1.4.3",
+        "@emnapi/runtime": "^1.4.3",
+        "@tybys/wasm-util": "^0.10.0"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz",
+      "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@nodable/entities": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz",
+      "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodable"
+        }
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@node-rs/bcrypt": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.10.7.tgz",
+      "integrity": "sha512-1wk0gHsUQC/ap0j6SJa2K34qNhomxXRcEe3T8cI5s+g6fgHBgLTN7U9LzWTG/HE6G4+2tWWLeCabk1wiYGEQSA==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      },
+      "optionalDependencies": {
+        "@node-rs/bcrypt-android-arm-eabi": "1.10.7",
+        "@node-rs/bcrypt-android-arm64": "1.10.7",
+        "@node-rs/bcrypt-darwin-arm64": "1.10.7",
+        "@node-rs/bcrypt-darwin-x64": "1.10.7",
+        "@node-rs/bcrypt-freebsd-x64": "1.10.7",
+        "@node-rs/bcrypt-linux-arm-gnueabihf": "1.10.7",
+        "@node-rs/bcrypt-linux-arm64-gnu": "1.10.7",
+        "@node-rs/bcrypt-linux-arm64-musl": "1.10.7",
+        "@node-rs/bcrypt-linux-x64-gnu": "1.10.7",
+        "@node-rs/bcrypt-linux-x64-musl": "1.10.7",
+        "@node-rs/bcrypt-wasm32-wasi": "1.10.7",
+        "@node-rs/bcrypt-win32-arm64-msvc": "1.10.7",
+        "@node-rs/bcrypt-win32-ia32-msvc": "1.10.7",
+        "@node-rs/bcrypt-win32-x64-msvc": "1.10.7"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-android-arm-eabi": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.10.7.tgz",
+      "integrity": "sha512-8dO6/PcbeMZXS3VXGEtct9pDYdShp2WBOWlDvSbcRwVqyB580aCBh0BEFmKYtXLzLvUK8Wf+CG3U6sCdILW1lA==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-android-arm64": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.10.7.tgz",
+      "integrity": "sha512-UASFBS/CucEMHiCtL/2YYsAY01ZqVR1N7vSb94EOvG5iwW7BQO06kXXCTgj+Xbek9azxixrCUmo3WJnkJZ0hTQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-darwin-arm64": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.10.7.tgz",
+      "integrity": "sha512-DgzFdAt455KTuiJ/zYIyJcKFobjNDR/hnf9OS7pK5NRS13Nq4gLcSIIyzsgHwZHxsJWbLpHmFc1H23Y7IQoQBw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-darwin-x64": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.10.7.tgz",
+      "integrity": "sha512-SPWVfQ6sxSokoUWAKWD0EJauvPHqOGQTd7CxmYatcsUgJ/bruvEHxZ4bIwX1iDceC3FkOtmeHO0cPwR480n/xA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-freebsd-x64": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.10.7.tgz",
+      "integrity": "sha512-gpa+Ixs6GwEx6U6ehBpsQetzUpuAGuAFbOiuLB2oo4N58yU4AZz1VIcWyWAHrSWRs92O0SHtmo2YPrMrwfBbSw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.10.7.tgz",
+      "integrity": "sha512-kYgJnTnpxrzl9sxYqzflobvMp90qoAlaX1oDL7nhNTj8OYJVDIk0jQgblj0bIkjmoPbBed53OJY/iu4uTS+wig==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-linux-arm64-gnu": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.10.7.tgz",
+      "integrity": "sha512-7cEkK2RA+gBCj2tCVEI1rDSJV40oLbSq7bQ+PNMHNI6jCoXGmj9Uzo7mg7ZRbNZ7piIyNH5zlJqutjo8hh/tmA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-linux-arm64-musl": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.10.7.tgz",
+      "integrity": "sha512-X7DRVjshhwxUqzdUKDlF55cwzh+wqWJ2E/tILvZPboO3xaNO07Um568Vf+8cmKcz+tiZCGP7CBmKbBqjvKN/Pw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-linux-x64-gnu": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.10.7.tgz",
+      "integrity": "sha512-LXRZsvG65NggPD12hn6YxVgH0W3VR5fsE/o1/o2D5X0nxKcNQGeLWnRzs5cP8KpoFOuk1ilctXQJn8/wq+Gn/Q==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-linux-x64-musl": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.10.7.tgz",
+      "integrity": "sha512-tCjHmct79OfcO3g5q21ME7CNzLzpw1MAsUXCLHLGWH+V6pp/xTvMbIcLwzkDj6TI3mxK6kehTn40SEjBkZ3Rog==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-wasm32-wasi": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.10.7.tgz",
+      "integrity": "sha512-4qXSihIKeVXYglfXZEq/QPtYtBUvR8d3S85k15Lilv3z5B6NSGQ9mYiNleZ7QHVLN2gEc5gmi7jM353DMH9GkA==",
+      "cpu": [
+        "wasm32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@napi-rs/wasm-runtime": "^0.2.5"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-win32-arm64-msvc": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.10.7.tgz",
+      "integrity": "sha512-FdfUQrqmDfvC5jFhntMBkk8EI+fCJTx/I1v7Rj+Ezlr9rez1j1FmuUnywbBj2Cg15/0BDhwYdbyZ5GCMFli2aQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-win32-ia32-msvc": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.10.7.tgz",
+      "integrity": "sha512-lZLf4Cx+bShIhU071p5BZft4OvP4PGhyp542EEsb3zk34U5GLsGIyCjOafcF/2DGewZL6u8/aqoxbSuROkgFXg==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@node-rs/bcrypt-win32-x64-msvc": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.10.7.tgz",
+      "integrity": "sha512-hdw7tGmN1DxVAMTzICLdaHpXjy+4rxaxnBMgI8seG1JL5e3VcRGsd1/1vVDogVp2cbsmgq+6d6yAY+D9lW/DCg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "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",
+      "optional": true,
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parse/fs-files-adapter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
+      "integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A==",
+      "license": "MIT"
+    },
+    "node_modules/@parse/node-apn": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-8.0.0.tgz",
+      "integrity": "sha512-blvU/V0FL3j7u2lstso1aInMw7yYrKg/6Ctr3Kc/7kleFatAfZswhzHk9d5lI4DUQBsUBun8nidgZHCY6sft+Q==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "4.4.3",
+        "jsonwebtoken": "9.0.3",
+        "node-forge": "1.4.0",
+        "verror": "1.10.1"
+      },
+      "engines": {
+        "node": "20 || 22 || 24"
+      }
+    },
+    "node_modules/@parse/push-adapter": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.4.0.tgz",
+      "integrity": "sha512-sWinUJZvbWIH6cJfIRuwUCcsjvi6IkoJ3zp2JoCP/mLzItt6NPNk+j73RE9UJzIKlwt3NciWXeSHoxprPnNH/A==",
+      "license": "MIT",
+      "dependencies": {
+        "@parse/node-apn": "8.0.0",
+        "expo-server-sdk": "6.1.0",
+        "firebase-admin": "13.7.0",
+        "npmlog": "7.0.1",
+        "parse": "8.5.0",
+        "web-push": "3.6.7"
+      },
+      "engines": {
+        "node": "20 || 22 || 24"
+      }
+    },
+    "node_modules/@parse/push-adapter/node_modules/@babel/runtime": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+      "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@parse/push-adapter/node_modules/@babel/runtime-corejs3": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz",
+      "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js-pure": "^3.48.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@parse/push-adapter/node_modules/parse": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/parse/-/parse-8.5.0.tgz",
+      "integrity": "sha512-X9gI4Yjbi9LPMPnCtKL4h0Nxe1aSCFMPWcB1zbu11qU/Be3eVSB5I5IMBunTuWlVz6Wchu3dtM5jl/1aBZ9wiQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@babel/runtime": "7.28.6",
+        "@babel/runtime-corejs3": "7.29.0",
+        "crypto-js": "4.2.0",
+        "idb-keyval": "6.2.2",
+        "react-native-crypto-js": "1.0.0",
+        "ws": "8.19.0"
+      },
+      "engines": {
+        "node": ">=20.19.0 <21 || >=22.12.0 <23 || >=24.1.0 <25"
+      }
+    },
+    "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/inquire": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz",
+      "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==",
+      "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/@redis/bloom": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.11.0.tgz",
+      "integrity": "sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@redis/client": "^5.11.0"
+      }
+    },
+    "node_modules/@redis/client": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.11.0.tgz",
+      "integrity": "sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==",
+      "license": "MIT",
+      "dependencies": {
+        "cluster-key-slot": "1.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@node-rs/xxhash": "^1.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@node-rs/xxhash": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@redis/json": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.11.0.tgz",
+      "integrity": "sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@redis/client": "^5.11.0"
+      }
+    },
+    "node_modules/@redis/search": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.11.0.tgz",
+      "integrity": "sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@redis/client": "^5.11.0"
+      }
+    },
+    "node_modules/@redis/time-series": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.11.0.tgz",
+      "integrity": "sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@redis/client": "^5.11.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "color": "^5.0.2",
+        "text-hex": "1.0.x"
+      }
+    },
+    "node_modules/@tootallnate/once": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz",
+      "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/busboy": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz",
+      "integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/caseless": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+      "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "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/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,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/express": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
+      "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^5.0.0",
+        "@types/serve-static": "^2"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz",
+      "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==",
+      "dev": true,
+      "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==",
+      "license": "MIT"
+    },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "9.0.10",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+      "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/long": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+      "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/@types/ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+      "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "24.13.3",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
+      "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.18.0"
+      }
+    },
+    "node_modules/@types/object-path": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.4.tgz",
+      "integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/pg": {
+      "version": "8.20.0",
+      "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
+      "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "pg-protocol": "*",
+        "pg-types": "^2.2.0"
+      }
+    },
+    "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==",
+      "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==",
+      "license": "MIT"
+    },
+    "node_modules/@types/request": {
+      "version": "2.48.13",
+      "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
+      "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@types/caseless": "*",
+        "@types/node": "*",
+        "@types/tough-cookie": "*",
+        "form-data": "^2.5.5"
+      }
+    },
+    "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": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/tough-cookie": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+      "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+      "license": "MIT",
+      "optional": true
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/@types/webidl-conversions": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+      "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/whatwg-url": {
+      "version": "13.0.0",
+      "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
+      "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/webidl-conversions": "*"
+      }
+    },
+    "node_modules/@typescript/typescript-aix-ppc64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
+      "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-darwin-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
+      "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-darwin-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
+      "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-freebsd-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
+      "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-freebsd-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
+      "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-arm": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
+      "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
+      "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-loong64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
+      "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-mips64el": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
+      "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-ppc64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
+      "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-riscv64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
+      "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-s390x": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
+      "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-linux-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
+      "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-netbsd-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
+      "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-netbsd-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
+      "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-openbsd-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
+      "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-openbsd-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
+      "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-sunos-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
+      "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-win32-arm64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
+      "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@typescript/typescript-win32-x64": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
+      "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=16.20.0"
+      }
+    },
+    "node_modules/@whatwg-node/promise-helpers": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
+      "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.6.3"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/abstract-logging": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+      "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
+      "license": "MIT"
+    },
+    "node_modules/accepts": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "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/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/ansi-styles/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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/ansi-styles/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==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/anynum": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+      "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/aproba": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
+      "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
+      "license": "ISC"
+    },
+    "node_modules/are-we-there-yet": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz",
+      "integrity": "sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg==",
+      "deprecated": "This package is no longer supported.",
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/asn1.js": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+      "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+      "license": "MIT",
+      "dependencies": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "node_modules/assert-options": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.3.tgz",
+      "integrity": "sha512-s6v4HnA+vYSGO4eZX+F+I3gvF74wPk+m6Z1Q3w1Dsg4Pnv/R24vhKAasoMVZGvDpOOfTg1Qz4ptZnEbuy95XsQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "license": "MIT"
+    },
+    "node_modules/async-retry": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
+      "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+      "license": "MIT",
+      "dependencies": {
+        "retry": "0.13.1"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+      "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+      "license": "MIT",
+      "dependencies": {
+        "possible-typed-array-names": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/backoff": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
+      "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
+      "license": "MIT",
+      "dependencies": {
+        "precond": "0.2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "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"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/bcryptjs": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
+      "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
+      "license": "BSD-3-Clause",
+      "bin": {
+        "bcrypt": "bin/bcrypt"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/bn.js": {
+      "version": "4.12.5",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz",
+      "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/bson": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.1.tgz",
+      "integrity": "sha512-h/C0qe6857pQhcSJHLfsR1uYGj98Ge3wKAD3Ed9KqH3wcVh+BM4Jq4xISD7vs9OPuT07n+q3QQVjslJ286j6ag==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
+      "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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/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==",
+      "license": "ISC",
+      "optional": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cluster-key-slot": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+      "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/color": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
+      "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^3.1.3",
+        "color-string": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.6"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz",
+      "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.20"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/color-support": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "license": "ISC",
+      "bin": {
+        "color-support": "bin.js"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "14.0.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+      "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+      "license": "ISC"
+    },
+    "node_modules/content-disposition": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+      "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/core-js-pure": {
+      "version": "3.49.0",
+      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz",
+      "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+      "license": "MIT"
+    },
+    "node_modules/cors": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+      "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/cross-inspect": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
+      "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "17.4.2",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
+      "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/duplexify": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+      "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "end-of-stream": "^1.4.1",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.1.1",
+        "stream-shift": "^1.0.2"
+      }
+    },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/err-code": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+      "license": "MIT"
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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,
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "optional": true,
+      "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==",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/expo-server-sdk": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-6.1.0.tgz",
+      "integrity": "sha512-ISuax1AQ7cpM5RAqcu8gVcoLL0ZKskJ5OLoMWmdITBe9nYjTucjdGyBq817YkIvTcj1pAUwx+9toUT7l/V7thA==",
+      "license": "MIT",
+      "dependencies": {
+        "promise-limit": "^2.7.0",
+        "promise-retry": "^2.0.1",
+        "undici": "^7.2.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/express": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+      "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+      "license": "MIT",
+      "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/express-rate-limit": {
+      "version": "8.3.1",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
+      "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
+      "license": "MIT",
+      "dependencies": {
+        "ip-address": "10.1.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/express-rate-limit"
+      },
+      "peerDependencies": {
+        "express": ">= 4.11"
+      }
+    },
+    "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/extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "license": "MIT"
+    },
+    "node_modules/farmhash-modern": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz",
+      "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "license": "MIT"
+    },
+    "node_modules/fast-xml-builder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz",
+      "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "path-expression-matcher": "^1.6.2",
+        "xml-naming": "^0.3.0"
+      }
+    },
+    "node_modules/fast-xml-parser": {
+      "version": "5.10.1",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz",
+      "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@nodable/entities": "^3.0.0",
+        "fast-xml-builder": "^1.2.0",
+        "is-unsafe": "^2.0.0",
+        "path-expression-matcher": "^1.6.2",
+        "strnum": "^2.4.1",
+        "xml-naming": "^0.3.0"
+      },
+      "bin": {
+        "fxparser": "src/cli/cli.js"
+      }
+    },
+    "node_modules/faye-websocket": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "websocket-driver": ">=0.5.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "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/file-stream-rotator": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
+      "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
+      "license": "MIT",
+      "dependencies": {
+        "moment": "^2.29.1"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+      "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+      "license": "MIT",
+      "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/firebase-admin": {
+      "version": "13.7.0",
+      "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.7.0.tgz",
+      "integrity": "sha512-o3qS8zCJbApe7aKzkO2Pa380t9cHISqeSd3blqYTtOuUUUua3qZTLwNWgGUOss3td6wbzrZhiHIj3c8+fC046Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@fastify/busboy": "^3.0.0",
+        "@firebase/database-compat": "^2.0.0",
+        "@firebase/database-types": "^1.0.6",
+        "farmhash-modern": "^1.1.0",
+        "fast-deep-equal": "^3.1.1",
+        "google-auth-library": "^10.6.1",
+        "jsonwebtoken": "^9.0.0",
+        "jwks-rsa": "^3.1.0",
+        "node-forge": "^1.3.1",
+        "uuid": "^11.0.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@google-cloud/firestore": "^7.11.0",
+        "@google-cloud/storage": "^7.19.0"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/for-each": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+      "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+      "license": "MIT",
+      "dependencies": {
+        "is-callable": "^1.2.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.6.tgz",
+      "integrity": "sha512-Ogz/E85h9tlfJzpI6TuFpGcHZFhLrb9Gw8wq9v40CxSCPnv7ahKr6Xgtkn0KYCDQJ8DNn5VoMO8EXr9V5PadyA==",
+      "license": "MIT",
+      "optional": true,
+      "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",
+        "safe-buffer": "^5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.12"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "optional": true,
+      "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==",
+      "license": "MIT",
+      "optional": true,
+      "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/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/fs-capacitor": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz",
+      "integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/gauge": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-5.0.2.tgz",
+      "integrity": "sha512-pMaFftXPtiGIHCJHdcUUx9Rby/rFT/Kkt3fIIGCs+9PMDIljSyRiqraTlxNtBReJRDfUefpa263RQ3vnp5G/LQ==",
+      "deprecated": "This package is no longer supported.",
+      "license": "ISC",
+      "dependencies": {
+        "aproba": "^1.0.3 || ^2.0.0",
+        "color-support": "^1.1.3",
+        "console-control-strings": "^1.1.0",
+        "has-unicode": "^2.0.1",
+        "signal-exit": "^4.0.1",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1",
+        "wide-align": "^1.1.5"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/gaxios": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
+      "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^7.0.1",
+        "is-stream": "^2.0.0",
+        "node-fetch": "^2.6.9",
+        "uuid": "^9.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/gaxios/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "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"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "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==",
+      "license": "ISC",
+      "optional": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/google-auth-library": {
+      "version": "10.9.0",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.0.tgz",
+      "integrity": "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "gaxios": "^7.1.4",
+        "gcp-metadata": "8.1.2",
+        "google-logging-utils": "1.1.3",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/google-auth-library/node_modules/gaxios": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.2.0.tgz",
+      "integrity": "sha512-CUVb4wcYe+771XevyH6HtGmXFAGGKkIC3kswAP8Z1JCe0j80JMaTPZH930DWFrvo0atjh18Arc0pEyUCWa5bfg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^7.0.1",
+        "node-fetch": "^3.3.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/google-auth-library/node_modules/gcp-metadata": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+      "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "gaxios": "^7.0.0",
+        "google-logging-utils": "^1.0.0",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/google-auth-library/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/google-gax": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz",
+      "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@grpc/grpc-js": "^1.10.9",
+        "@grpc/proto-loader": "^0.7.13",
+        "@types/long": "^4.0.0",
+        "abort-controller": "^3.0.0",
+        "duplexify": "^4.0.0",
+        "google-auth-library": "^9.3.0",
+        "node-fetch": "^2.7.0",
+        "object-hash": "^3.0.0",
+        "proto3-json-serializer": "^2.0.2",
+        "protobufjs": "^7.3.2",
+        "retry-request": "^7.0.0",
+        "uuid": "^9.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/google-gax/node_modules/gcp-metadata": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+      "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "gaxios": "^6.1.1",
+        "google-logging-utils": "^0.0.2",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/google-gax/node_modules/google-auth-library": {
+      "version": "9.15.1",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+      "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "gaxios": "^6.1.1",
+        "gcp-metadata": "^6.1.0",
+        "gtoken": "^7.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/google-gax/node_modules/google-logging-utils": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+      "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/google-gax/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "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"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graphql": {
+      "version": "16.13.2",
+      "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz",
+      "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+      }
+    },
+    "node_modules/graphql-list-fields": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/graphql-list-fields/-/graphql-list-fields-2.0.4.tgz",
+      "integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA==",
+      "license": "MIT"
+    },
+    "node_modules/graphql-relay": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz",
+      "integrity": "sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.15.0 || >= 15.9.0"
+      },
+      "peerDependencies": {
+        "graphql": "^16.2.0"
+      }
+    },
+    "node_modules/gtoken": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+      "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "gaxios": "^6.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+      "license": "ISC"
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/html-entities": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
+      "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/mdevils"
+        },
+        {
+          "type": "patreon",
+          "url": "https://patreon.com/mdevils"
+        }
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/http_ece": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
+      "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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/http-parser-js": {
+      "version": "0.5.10",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+      "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+      "license": "MIT"
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@tootallnate/once": "2",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/http-proxy-agent/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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "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/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==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/idb-keyval": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+      "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/intersect": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz",
+      "integrity": "sha512-qsc720yevCO+4NydrJWgEWKccAQwTOvj2m73O/VBA6iUL2HGZJ9XqBiyraNrBXX/W1IAjdpXdRZk24sq8TzBRg==",
+      "license": "MIT"
+    },
+    "node_modules/ip-address": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+      "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-promise": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+      "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "dependencies": {
+        "which-typed-array": "^1.1.16"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-unsafe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz",
+      "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "license": "MIT"
+    },
+    "node_modules/jose": {
+      "version": "4.15.9",
+      "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
+      "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/panva"
+      }
+    },
+    "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/jsonwebtoken": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+      "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+      "license": "MIT",
+      "dependencies": {
+        "jws": "^4.0.1",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/jwa": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jwks-rsa": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz",
+      "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "^4.17.20",
+        "@types/jsonwebtoken": "^9.0.4",
+        "debug": "^4.3.4",
+        "jose": "^4.15.4",
+        "limiter": "^1.1.5",
+        "lru-memoizer": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/jwks-rsa/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==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "^1"
+      }
+    },
+    "node_modules/jwks-rsa/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==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/jwks-rsa/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==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/jwks-rsa/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==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*",
+        "@types/send": "<1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+      "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/kuler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+      "license": "MIT"
+    },
+    "node_modules/ldapjs": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz",
+      "integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==",
+      "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
+      "license": "MIT",
+      "dependencies": {
+        "@ldapjs/asn1": "^2.0.0",
+        "@ldapjs/attribute": "^1.0.0",
+        "@ldapjs/change": "^1.0.0",
+        "@ldapjs/controls": "^2.1.0",
+        "@ldapjs/dn": "^1.1.0",
+        "@ldapjs/filter": "^2.1.1",
+        "@ldapjs/messages": "^1.3.0",
+        "@ldapjs/protocol": "^1.2.1",
+        "abstract-logging": "^2.0.1",
+        "assert-plus": "^1.0.0",
+        "backoff": "^2.5.0",
+        "once": "^1.4.0",
+        "vasync": "^2.2.1",
+        "verror": "^1.10.1"
+      }
+    },
+    "node_modules/limiter": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+      "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "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",
+      "optional": true
+    },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "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/loglevel": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
+      "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      },
+      "funding": {
+        "type": "tidelift",
+        "url": "https://tidelift.com/funding/github/npm/loglevel"
+      }
+    },
+    "node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/lru-cache": {
+      "version": "11.2.7",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+      "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/lru-memoizer": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz",
+      "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==",
+      "license": "MIT",
+      "dependencies": {
+        "lodash.clonedeep": "^4.5.0",
+        "lru-cache": "6.0.0"
+      }
+    },
+    "node_modules/lru-memoizer/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "license": "MIT"
+    },
+    "node_modules/merge-descriptors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mime": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+      "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
+      "funding": [
+        "https://github.com/sponsors/broofa"
+      ],
+      "license": "MIT",
+      "bin": {
+        "mime": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "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==",
+      "license": "ISC"
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/moment": {
+      "version": "2.30.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+      "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mongodb": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.0.tgz",
+      "integrity": "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@mongodb-js/saslprep": "^1.3.0",
+        "bson": "^7.1.1",
+        "mongodb-connection-string-url": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@aws-sdk/credential-providers": "^3.806.0",
+        "@mongodb-js/zstd": "^7.0.0",
+        "gcp-metadata": "^7.0.1",
+        "kerberos": "^7.0.0",
+        "mongodb-client-encryption": ">=7.0.0 <7.1.0",
+        "snappy": "^7.3.2",
+        "socks": "^2.8.6"
+      },
+      "peerDependenciesMeta": {
+        "@aws-sdk/credential-providers": {
+          "optional": true
+        },
+        "@mongodb-js/zstd": {
+          "optional": true
+        },
+        "gcp-metadata": {
+          "optional": true
+        },
+        "kerberos": {
+          "optional": true
+        },
+        "mongodb-client-encryption": {
+          "optional": true
+        },
+        "snappy": {
+          "optional": true
+        },
+        "socks": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/mongodb-connection-string-url": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.2.tgz",
+      "integrity": "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/whatwg-url": "^13.0.0",
+        "whatwg-url": "^14.1.0"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+      "license": "MIT",
+      "bin": {
+        "mustache": "bin/mustache"
+      }
+    },
+    "node_modules/negotiator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "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-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-fetch/node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/node-fetch/node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause",
+      "optional": true
+    },
+    "node_modules/node-fetch/node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/node-forge": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+      "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+      "license": "(BSD-3-Clause OR GPL-2.0)",
+      "engines": {
+        "node": ">= 6.13.0"
+      }
+    },
+    "node_modules/npmlog": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-7.0.1.tgz",
+      "integrity": "sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg==",
+      "deprecated": "This package is no longer supported.",
+      "license": "ISC",
+      "dependencies": {
+        "are-we-there-yet": "^4.0.0",
+        "console-control-strings": "^1.1.0",
+        "gauge": "^5.0.0",
+        "set-blocking": "^2.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object-path": {
+      "version": "0.11.8",
+      "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz",
+      "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.12.0"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "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==",
+      "license": "MIT",
+      "dependencies": {
+        "fn.name": "1.x.x"
+      }
+    },
+    "node_modules/otpauth": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.5.0.tgz",
+      "integrity": "sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/hectorm/otpauth?sponsor=1"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parse": {
+      "version": "8.6.0",
+      "resolved": "https://registry.npmjs.org/parse/-/parse-8.6.0.tgz",
+      "integrity": "sha512-AZjc8yGo8/iTZFpCXWw/r1qNusiUGWtq9i92/u0jNd+Iupg3EJUSV/OOyTrCeav8NDyo92wVS5O3iKAYPlhlsA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@babel/runtime": "7.29.2",
+        "@babel/runtime-corejs3": "7.29.2",
+        "crypto-js": "4.2.0",
+        "idb-keyval": "6.2.2",
+        "react-native-crypto-js": "1.0.0",
+        "ws": "8.20.0"
+      },
+      "engines": {
+        "node": ">=20.19.0 <21 || >=22.13.0 <23 || >=24.1.0 <25"
+      }
+    },
+    "node_modules/parse-server": {
+      "version": "9.10.0",
+      "resolved": "https://registry.npmjs.org/parse-server/-/parse-server-9.10.0.tgz",
+      "integrity": "sha512-f4IIsWLsh30mOJl6u6SB3GBrj7Zj4f69dKkI0PPZxNeVttlKFBEg/VHc2AQrWKWRvnIlWy22o2UdX4ECPDPCIw==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@apollo/server": "5.5.0",
+        "@as-integrations/express5": "1.1.2",
+        "@fastify/busboy": "3.2.0",
+        "@graphql-tools/merge": "9.1.7",
+        "@graphql-tools/schema": "10.0.31",
+        "@graphql-tools/utils": "11.0.0",
+        "@parse/fs-files-adapter": "3.0.0",
+        "@parse/push-adapter": "8.4.0",
+        "bcryptjs": "3.0.3",
+        "commander": "14.0.3",
+        "cors": "2.8.6",
+        "express": "5.2.1",
+        "express-rate-limit": "8.3.1",
+        "follow-redirects": "1.15.11",
+        "graphql": "16.13.2",
+        "graphql-list-fields": "2.0.4",
+        "graphql-relay": "0.10.2",
+        "graphql-upload": "15.0.2",
+        "intersect": "1.0.1",
+        "jsonwebtoken": "9.0.3",
+        "jwks-rsa": "3.2.0",
+        "ldapjs": "3.0.7",
+        "lodash": "4.18.1",
+        "lru-cache": "11.2.7",
+        "mime": "4.1.0",
+        "mongodb": "7.1.0",
+        "mustache": "4.2.0",
+        "otpauth": "9.5.0",
+        "parse": "8.6.0",
+        "path-to-regexp": "8.4.2",
+        "pg-monitor": "3.1.0",
+        "pg-promise": "12.6.0",
+        "pluralize": "8.0.0",
+        "punycode": "2.3.1",
+        "rate-limit-redis": "4.3.1",
+        "redis": "5.11.0",
+        "semver": "7.7.4",
+        "tv4": "1.3.0",
+        "winston": "3.19.0",
+        "winston-daily-rotate-file": "5.0.0",
+        "ws": "8.20.0"
+      },
+      "bin": {
+        "parse-server": "bin/parse-server"
+      },
+      "engines": {
+        "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.11.0 <25.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parse-server"
+      },
+      "optionalDependencies": {
+        "@node-rs/bcrypt": "1.10.7"
+      }
+    },
+    "node_modules/parse-server/node_modules/graphql-upload": {
+      "version": "15.0.2",
+      "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-15.0.2.tgz",
+      "integrity": "sha512-ufJAkZJBKWRDD/4wJR3VZMy9QWTwqIYIciPtCEF5fCNgWF+V1p7uIgz+bP2YYLiS4OJBhCKR8rnqE/Wg3XPUiw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/busboy": "^1.5.0",
+        "@types/node": "*",
+        "@types/object-path": "^0.11.1",
+        "busboy": "^1.6.0",
+        "fs-capacitor": "^6.2.0",
+        "http-errors": "^2.0.0",
+        "object-path": "^0.11.8"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jaydenseric"
+      },
+      "peerDependencies": {
+        "@types/express": "^4.0.29",
+        "@types/koa": "^2.11.4",
+        "graphql": "^16.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/express": {
+          "optional": true
+        },
+        "@types/koa": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-expression-matcher": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
+      "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/pg": {
+      "version": "8.22.0",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz",
+      "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-connection-string": "^2.14.0",
+        "pg-pool": "^3.14.0",
+        "pg-protocol": "^1.15.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-cloudflare/-/pg-cloudflare-1.4.0.tgz",
+      "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
+      "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-cursor": {
+      "version": "2.21.0",
+      "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.21.0.tgz",
+      "integrity": "sha512-IYvk/j+Suhtbo/C3uOf4JLsLK/gWxOTUOmYbDsbKnLaVJDq+KwhwK6ngpRfiCk8eDMS3AmGQABZCv0cREEzHQw==",
+      "license": "MIT",
+      "peer": true,
+      "peerDependencies": {
+        "pg": "^8"
+      }
+    },
+    "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-minify": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.8.0.tgz",
+      "integrity": "sha512-jO/oJOununpx8DzKgvSsWm61P8JjwXlaxSlbbfTBo1nvSWoo/+I6qZYaSN96jm/KDwa5d+JMQwPGgcP6HXDRow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/pg-monitor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-3.1.0.tgz",
+      "integrity": "sha512-giK0h52AOO/v8iu6hZCdZ/X9W8oAM9Dm1VReQQtki532X8g4z1LVIm4Z/3cGvDcETWW+Ty0FrtU8iTrGFYIZfA==",
+      "license": "MIT",
+      "dependencies": {
+        "picocolors": "1.1.1"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "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-promise": {
+      "version": "12.6.0",
+      "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-12.6.0.tgz",
+      "integrity": "sha512-ZnfNn7c0U2p1OWYqoENcke8eSTe+yCGOpuMurExTuot/pe3POodbakE9Sj5MWUsyPpyreARUUPwe4j/5Dfs9Dw==",
+      "license": "MIT",
+      "dependencies": {
+        "assert-options": "0.8.3",
+        "pg": "8.18.0",
+        "pg-minify": "1.8.0",
+        "spex": "4.1.0"
+      },
+      "engines": {
+        "node": ">=16.0"
+      },
+      "peerDependencies": {
+        "pg-query-stream": "4.12.0"
+      }
+    },
+    "node_modules/pg-promise/node_modules/pg": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
+      "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-connection-string": "^2.11.0",
+        "pg-pool": "^3.11.0",
+        "pg-protocol": "^1.11.0",
+        "pg-types": "2.2.0",
+        "pgpass": "1.0.5"
+      },
+      "engines": {
+        "node": ">= 16.0.0"
+      },
+      "optionalDependencies": {
+        "pg-cloudflare": "^1.3.0"
+      },
+      "peerDependencies": {
+        "pg-native": ">=3.0.1"
+      },
+      "peerDependenciesMeta": {
+        "pg-native": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pg-protocol": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz",
+      "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==",
+      "license": "MIT"
+    },
+    "node_modules/pg-query-stream": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.12.0.tgz",
+      "integrity": "sha512-H97oiVPQ0+eRqIFOeYMUnjDcv9od7vHHMjiVDAhg2SEzAUr3M/dT83UEV1B+fm+tcVnymI8j2LSp57/+yjF6Fg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "pg-cursor": "^2.17.0"
+      },
+      "peerDependencies": {
+        "pg": "^8"
+      }
+    },
+    "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.5",
+      "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+      "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+      "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==",
+      "license": "ISC"
+    },
+    "node_modules/pluralize": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+      "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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/precond": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
+      "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/process-warning": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz",
+      "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==",
+      "license": "MIT"
+    },
+    "node_modules/promise-limit": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
+      "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==",
+      "license": "ISC"
+    },
+    "node_modules/promise-retry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+      "license": "MIT",
+      "dependencies": {
+        "err-code": "^2.0.2",
+        "retry": "^0.12.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/promise-retry/node_modules/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/proto3-json-serializer": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
+      "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "protobufjs": "^7.2.5"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "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",
+      "optional": true,
+      "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/protobufjs/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",
+      "optional": true
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.15.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+      "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+      "license": "BSD-3-Clause",
+      "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/range-parser": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
+      "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/rate-limit-redis": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.3.1.tgz",
+      "integrity": "sha512-+a1zU8+D7L8siDK9jb14refQXz60vq427VuiplgnaLk9B2LnvGe/APLTfhwb4uNIL7eWVknh8GnRp/unCj+lMA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "peerDependencies": {
+        "express-rate-limit": ">= 6"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/redis": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/redis/-/redis-5.11.0.tgz",
+      "integrity": "sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@redis/bloom": "5.11.0",
+        "@redis/client": "5.11.0",
+        "@redis/json": "5.11.0",
+        "@redis/search": "5.11.0",
+        "@redis/time-series": "5.11.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/retry-request": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+      "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@types/request": "^2.48.8",
+        "extend": "^3.0.2",
+        "teeny-request": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/router": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+      "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+      "license": "MIT",
+      "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/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"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "license": "ISC",
+      "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+      "license": "ISC"
+    },
+    "node_modules/set-function-length": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
+        "gopd": "^1.0.1",
+        "has-property-descriptors": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "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==",
+      "license": "(MIT AND BSD-3-Clause)",
+      "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/side-channel": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+      "license": "MIT",
+      "dependencies": {
+        "memory-pager": "^1.0.2"
+      }
+    },
+    "node_modules/spex": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/spex/-/spex-4.1.0.tgz",
+      "integrity": "sha512-ktgNAQ1X9x1A3IMChM6XBDeVjhGPbLgPQ8aEzGOaUIhZTnLeJSBApvi3gXT789hee6h73N3jOeWkXDwoPbYT/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.0.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/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==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/stream-events": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+      "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "stubs": "^3.0.0"
+      }
+    },
+    "node_modules/stream-shift": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+      "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strnum": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+      "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "anynum": "^1.0.1"
+      }
+    },
+    "node_modules/stubs": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+      "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/teeny-request": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
+      "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "http-proxy-agent": "^5.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "node-fetch": "^2.6.9",
+        "stream-events": "^1.0.5",
+        "uuid": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/teeny-request/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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/teeny-request/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==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/teeny-request/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "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"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "isarray": "^2.0.5",
+        "safe-buffer": "^5.2.1",
+        "typed-array-buffer": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+      "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "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,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.28.0"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/tv4": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+      "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==",
+      "license": [
+        {
+          "type": "Public Domain",
+          "url": "http://geraintluff.github.io/tv4/LICENSE.txt"
+        },
+        {
+          "type": "MIT",
+          "url": "http://jsonary.com/LICENSE.txt"
+        }
+      ],
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "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==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.14"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
+      "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc"
+      },
+      "engines": {
+        "node": ">=16.20.0"
+      },
+      "optionalDependencies": {
+        "@typescript/typescript-aix-ppc64": "7.0.2",
+        "@typescript/typescript-darwin-arm64": "7.0.2",
+        "@typescript/typescript-darwin-x64": "7.0.2",
+        "@typescript/typescript-freebsd-arm64": "7.0.2",
+        "@typescript/typescript-freebsd-x64": "7.0.2",
+        "@typescript/typescript-linux-arm": "7.0.2",
+        "@typescript/typescript-linux-arm64": "7.0.2",
+        "@typescript/typescript-linux-loong64": "7.0.2",
+        "@typescript/typescript-linux-mips64el": "7.0.2",
+        "@typescript/typescript-linux-ppc64": "7.0.2",
+        "@typescript/typescript-linux-riscv64": "7.0.2",
+        "@typescript/typescript-linux-s390x": "7.0.2",
+        "@typescript/typescript-linux-x64": "7.0.2",
+        "@typescript/typescript-netbsd-arm64": "7.0.2",
+        "@typescript/typescript-netbsd-x64": "7.0.2",
+        "@typescript/typescript-openbsd-arm64": "7.0.2",
+        "@typescript/typescript-openbsd-x64": "7.0.2",
+        "@typescript/typescript-sunos-x64": "7.0.2",
+        "@typescript/typescript-win32-arm64": "7.0.2",
+        "@typescript/typescript-win32-x64": "7.0.2"
+      }
+    },
+    "node_modules/undici": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
+      "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.18.1"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "7.18.2",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+      "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+      "license": "MIT"
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/uuid": {
+      "version": "11.1.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz",
+      "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/esm/bin/uuid"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vasync": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
+      "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "verror": "1.10.0"
+      }
+    },
+    "node_modules/vasync/node_modules/verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "node_modules/verror": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
+      "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=0.6.0"
+      }
+    },
+    "node_modules/web-push": {
+      "version": "3.6.7",
+      "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz",
+      "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==",
+      "license": "MPL-2.0",
+      "dependencies": {
+        "asn1.js": "^5.3.0",
+        "http_ece": "1.2.0",
+        "https-proxy-agent": "^7.0.0",
+        "jws": "^4.0.0",
+        "minimist": "^1.2.5"
+      },
+      "bin": {
+        "web-push": "src/cli.js"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "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/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/websocket-driver": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz",
+      "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "http-parser-js": ">=0.5.1",
+        "safe-buffer": ">=5.1.0",
+        "websocket-extensions": ">=0.1.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/websocket-extensions": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+      "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "^5.1.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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/wide-align": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+      "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^1.0.2 || 2 || 3 || 4"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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-daily-rotate-file": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz",
+      "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==",
+      "license": "MIT",
+      "dependencies": {
+        "file-stream-rotator": "^0.6.1",
+        "object-hash": "^3.0.0",
+        "triple-beam": "^1.4.1",
+        "winston-transport": "^4.7.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "peerDependencies": {
+        "winston": "^3"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "logform": "^2.7.0",
+        "readable-stream": "^3.6.2",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "optional": true,
+      "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==",
+      "license": "ISC"
+    },
+    "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/xml-naming": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz",
+      "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "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==",
+      "license": "ISC",
+      "optional": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "license": "ISC"
+    },
+    "node_modules/yargs": {
+      "version": "17.7.3",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
+      "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
+      "license": "MIT",
+      "optional": true,
+      "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==",
+      "license": "ISC",
+      "optional": true,
+      "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==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zod": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+      "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    }
+  }
+}

+ 39 - 0
package.json

@@ -0,0 +1,39 @@
+{
+  "name": "saas-voc-server",
+  "version": "0.1.0",
+  "private": true,
+  "type": "module",
+  "description": "Independent domestic ecommerce VOC backend template",
+  "engines": {
+    "node": ">=22.13.0 <23"
+  },
+  "scripts": {
+    "build": "tsc -p tsconfig.json",
+    "dev": "tsx watch src/server.ts",
+    "start": "node dist/src/server.js",
+    "migrate": "tsx scripts/migrate.ts",
+    "import:dataset": "tsx scripts/import-dataset.ts",
+    "test": "tsx --test test/**/*.test.ts",
+    "test:coverage": "tsx --test --experimental-test-coverage test/**/*.test.ts",
+    "typecheck": "tsc -p tsconfig.json --noEmit"
+  },
+  "dependencies": {
+    "cors": "^2.8.6",
+    "dotenv": "^17.2.3",
+    "express": "^5.2.1",
+    "parse-server": "^9.10.0",
+    "pg": "^8.22.0",
+    "zod": "^4.4.3"
+  },
+  "devDependencies": {
+    "@types/cors": "^2.8.19",
+    "@types/express": "^5.0.6",
+    "@types/node": "^24.10.0",
+    "@types/pg": "^8.16.0",
+    "tsx": "^4.23.1",
+    "typescript": "^7.0.2"
+  },
+  "overrides": {
+    "ws": "8.21.0"
+  }
+}

+ 22 - 0
scripts/import-dataset.ts

@@ -0,0 +1,22 @@
+import 'dotenv/config';
+import { readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { loadConfig } from '../src/config/env.js';
+import { createDatabasePool } from '../src/db/pool.js';
+import { DatasetImportService, type ImportDataset } from '../src/modules/domestic-voc/services/dataset-import.service.js';
+
+const inputPath = process.argv[2];
+if (!inputPath) throw new Error('Usage: npm run import:dataset -- <dataset.json> [workspaceId]');
+
+const workspaceId = process.argv[3] || 'demashi';
+const dataset = JSON.parse(await readFile(resolve(inputPath), 'utf8')) as ImportDataset;
+const config = loadConfig();
+const pool = createDatabasePool(config, { applicationName: 'saas-voc-dataset-import' });
+
+try {
+  const result = await new DatasetImportService(pool).import(dataset, workspaceId);
+  console.log(JSON.stringify(result, null, 2));
+} finally {
+  await pool.end();
+}
+

+ 17 - 0
scripts/migrate.ts

@@ -0,0 +1,17 @@
+import 'dotenv/config';
+import { loadConfig } from '../src/config/env.js';
+import { createDatabasePool } from '../src/db/pool.js';
+import { runMigrations } from '../src/db/migrations.js';
+
+const config = loadConfig();
+const pool = createDatabasePool(config, {
+  connectionString: config.database.migrationUrl,
+  applicationName: 'saas-voc-migration',
+});
+
+try {
+  const applied = await runMigrations(pool);
+  console.log(applied.length ? `Applied migrations: ${applied.join(', ')}` : 'Database is up to date');
+} finally {
+  await pool.end();
+}

+ 82 - 0
src/app.ts

@@ -0,0 +1,82 @@
+import cors from 'cors';
+import express, { type ErrorRequestHandler, type RequestHandler } from 'express';
+import type { Pool } from 'pg';
+import { ZodError } from 'zod';
+import type { AppConfig } from './config/env.js';
+import { createDomesticVocRouter } from './modules/domestic-voc/routes.js';
+import { SyncJobRepository } from './modules/domestic-voc/repositories/sync-job.repository.js';
+import { SnapshotService } from './modules/domestic-voc/services/snapshot.service.js';
+import { SyncService } from './modules/domestic-voc/services/sync.service.js';
+
+export function createApp(input: {
+  config: AppConfig;
+  pool: Pool;
+  parseApp?: RequestHandler;
+}) {
+  const app = express();
+  app.disable('x-powered-by');
+  app.use(cors({
+    origin(origin, callback) {
+      if (!origin || input.config.corsOrigins.includes(origin)) return callback(null, true);
+      return callback(new Error('Origin is not allowed'));
+    },
+    credentials: true,
+  }));
+  app.use(express.json({ limit: '1mb' }));
+
+  app.get('/health', async (_request, response) => {
+    try {
+      const result = await input.pool.query<{ schema_ready: string | null }>(
+        "SELECT to_regclass('voc.workspace')::text AS schema_ready",
+      );
+      if (!result.rows[0]?.schema_ready) {
+        response.status(503).json({
+          service: 'saas-voc-server',
+          status: 'unavailable',
+          database: 'migration_required',
+          timestamp: new Date().toISOString(),
+        });
+        return;
+      }
+      response.json({
+        service: 'saas-voc-server',
+        status: 'ok',
+        database: 'ready',
+        timestamp: new Date().toISOString(),
+      });
+    } catch {
+      response.status(503).json({
+        service: 'saas-voc-server',
+        status: 'unavailable',
+        database: 'unavailable',
+        timestamp: new Date().toISOString(),
+      });
+    }
+  });
+
+  if (input.parseApp) app.use('/parse', input.parseApp);
+
+  const jobs = new SyncJobRepository(input.pool);
+  const sync = new SyncService(jobs);
+  const snapshot = new SnapshotService(input.pool);
+  app.use('/api/domestic-voc', createDomesticVocRouter({ jobs, sync, snapshot }));
+
+  app.use((_request, response) => {
+    response.status(404).json({ error: 'not_found' });
+  });
+
+  const errorHandler: ErrorRequestHandler = (error, _request, response, _next) => {
+    if (error instanceof ZodError) {
+      response.status(400).json({
+        error: 'invalid_request',
+        issues: error.issues.map((issue) => ({ path: issue.path.join('.'), message: issue.message })),
+      });
+      return;
+    }
+    console.error('[http] unhandled request error', error);
+    response.status(500).json({ error: 'internal_error' });
+  };
+  app.use(errorHandler);
+
+  return app;
+}

+ 92 - 0
src/config/env.ts

@@ -0,0 +1,92 @@
+import { z } from 'zod';
+
+const environmentSchema = z.object({
+  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
+  HOST: z.string().min(1).default('127.0.0.1'),
+  PORT: z.coerce.number().int().min(1).max(65535).default(4400),
+  DATABASE_URL: z.string().min(1, 'DATABASE_URL is required'),
+  MIGRATION_DATABASE_URL: z.string().min(1).optional(),
+  DATABASE_POOL_MAX: z.coerce.number().int().min(1).max(50).default(10),
+  DATABASE_IDLE_TIMEOUT_MS: z.coerce.number().int().min(1000).default(30_000),
+  DATABASE_STATEMENT_TIMEOUT_MS: z.coerce.number().int().min(1000).default(30_000),
+  PARSE_APP_ID: z.string().min(8, 'PARSE_APP_ID must contain at least 8 characters'),
+  PARSE_MASTER_KEY: z.string().min(32, 'PARSE_MASTER_KEY must contain at least 32 characters'),
+  PARSE_MAINTENANCE_KEY: z.string().min(32, 'PARSE_MAINTENANCE_KEY must contain at least 32 characters'),
+  PARSE_SERVER_URL: z.url(),
+  FMODE_BASE_URL: z.url(),
+  FMODE_API_KEY: z.string().min(1, 'FMODE_API_KEY is required'),
+  FMODE_TIMEOUT_MS: z.coerce.number().int().min(100).max(120_000).default(30_000),
+  FMODE_RETRIES: z.coerce.number().int().min(0).max(5).default(2),
+  CORS_ORIGINS: z.string().min(1),
+});
+
+export type AppConfig = {
+  nodeEnv: 'development' | 'test' | 'production';
+  host: string;
+  port: number;
+  database: {
+    url: string;
+    migrationUrl: string;
+    poolMax: number;
+    idleTimeoutMs: number;
+    statementTimeoutMs: number;
+  };
+  parse: {
+    appId: string;
+    masterKey: string;
+    maintenanceKey: string;
+    serverUrl: string;
+  };
+  fmode: {
+    baseUrl: string;
+    apiKey: string;
+    timeoutMs: number;
+    retries: number;
+  };
+  corsOrigins: string[];
+};
+
+export class EnvironmentConfigurationError extends Error {
+  constructor(public readonly issues: string[]) {
+    super(`Invalid environment configuration: ${issues.join('; ')}`);
+    this.name = 'EnvironmentConfigurationError';
+  }
+}
+
+export function loadConfig(environment: NodeJS.ProcessEnv = process.env): AppConfig {
+  const result = environmentSchema.safeParse(environment);
+  if (!result.success) {
+    const issues = result.error.issues.map((issue) => {
+      const field = issue.path.join('.') || 'environment';
+      return `${field}: ${issue.message}`;
+    });
+    throw new EnvironmentConfigurationError(issues);
+  }
+
+  const value = result.data;
+  return {
+    nodeEnv: value.NODE_ENV,
+    host: value.HOST,
+    port: value.PORT,
+    database: {
+      url: value.DATABASE_URL,
+      migrationUrl: value.MIGRATION_DATABASE_URL || value.DATABASE_URL,
+      poolMax: value.DATABASE_POOL_MAX,
+      idleTimeoutMs: value.DATABASE_IDLE_TIMEOUT_MS,
+      statementTimeoutMs: value.DATABASE_STATEMENT_TIMEOUT_MS,
+    },
+    parse: {
+      appId: value.PARSE_APP_ID,
+      masterKey: value.PARSE_MASTER_KEY,
+      maintenanceKey: value.PARSE_MAINTENANCE_KEY,
+      serverUrl: value.PARSE_SERVER_URL,
+    },
+    fmode: {
+      baseUrl: value.FMODE_BASE_URL.replace(/\/+$/, ''),
+      apiKey: value.FMODE_API_KEY,
+      timeoutMs: value.FMODE_TIMEOUT_MS,
+      retries: value.FMODE_RETRIES,
+    },
+    corsOrigins: value.CORS_ORIGINS.split(',').map((origin) => origin.trim()).filter(Boolean),
+  };
+}

+ 18 - 0
src/config/parse.ts

@@ -0,0 +1,18 @@
+import { ParseServer } from 'parse-server';
+import type { AppConfig } from './env.js';
+
+export async function createParseServer(config: AppConfig): Promise<ReturnType<typeof ParseServer>> {
+  const server = ParseServer({
+    databaseURI: config.database.url,
+    appId: config.parse.appId,
+    masterKey: config.parse.masterKey,
+    maintenanceKey: config.parse.maintenanceKey,
+    serverURL: config.parse.serverUrl,
+    allowClientClassCreation: false,
+    allowCustomObjectId: false,
+    enforcePrivateUsers: true,
+    masterKeyIps: ['127.0.0.1', '::1'],
+  });
+  await server.start();
+  return server;
+}

+ 45 - 0
src/db/migrations.ts

@@ -0,0 +1,45 @@
+import { readdir, readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import type { Pool, PoolClient } from 'pg';
+
+async function ensureMigrationTable(client: PoolClient): Promise<void> {
+  await client.query('CREATE SCHEMA IF NOT EXISTS voc');
+  await client.query(`
+    CREATE TABLE IF NOT EXISTS voc.schema_migration (
+      version text PRIMARY KEY,
+      applied_at timestamptz NOT NULL DEFAULT now()
+    )
+  `);
+}
+
+export async function runMigrations(pool: Pool, directory = resolve(process.cwd(), 'migrations')): Promise<string[]> {
+  const client = await pool.connect();
+  const applied: string[] = [];
+  try {
+    await ensureMigrationTable(client);
+    const files = (await readdir(directory)).filter((file) => file.endsWith('.sql')).sort();
+    for (const file of files) {
+      const exists = await client.query<{ version: string }>(
+        'SELECT version FROM voc.schema_migration WHERE version = $1',
+        [file],
+      );
+      if (exists.rowCount) continue;
+
+      const sql = await readFile(resolve(directory, file), 'utf8');
+      await client.query('BEGIN');
+      try {
+        await client.query(sql);
+        await client.query('INSERT INTO voc.schema_migration (version) VALUES ($1)', [file]);
+        await client.query('COMMIT');
+        applied.push(file);
+      } catch (error) {
+        await client.query('ROLLBACK');
+        throw error;
+      }
+    }
+  } finally {
+    client.release();
+  }
+  return applied;
+}
+

+ 22 - 0
src/db/pool.ts

@@ -0,0 +1,22 @@
+import { Pool } from 'pg';
+import type { AppConfig } from '../config/env.js';
+
+export function createDatabasePool(
+  config: AppConfig,
+  options: { connectionString?: string; applicationName?: string } = {},
+): Pool {
+  const pool = new Pool({
+    connectionString: options.connectionString ?? config.database.url,
+    max: config.database.poolMax,
+    idleTimeoutMillis: config.database.idleTimeoutMs,
+    connectionTimeoutMillis: 5_000,
+    statement_timeout: config.database.statementTimeoutMs,
+    application_name: options.applicationName ?? 'saas-voc-server',
+  });
+
+  pool.on('error', (error) => {
+    console.error('[database] idle client error', error);
+  });
+
+  return pool;
+}

+ 9 - 0
src/db/types.ts

@@ -0,0 +1,9 @@
+import type { QueryResult, QueryResultRow } from 'pg';
+
+export interface Queryable {
+  query<R extends QueryResultRow = QueryResultRow>(
+    text: string,
+    values?: readonly unknown[],
+  ): Promise<QueryResult<R>>;
+}
+

+ 54 - 0
src/modules/domestic-voc/domain/identity.ts

@@ -0,0 +1,54 @@
+import { createHash } from 'node:crypto';
+
+function normalizePart(value: string, field: string): string {
+  const normalized = value.trim();
+  if (!normalized) throw new Error(`${field} cannot be empty`);
+  return normalized;
+}
+
+export function makeProductKey(platform: string, productId: string): string {
+  return `${normalizePart(platform, 'platform').toLowerCase()}:${normalizePart(productId, 'productId')}`;
+}
+
+export function makeRelationKey(platform: string, ownProductId: string, competitorProductId: string): string {
+  return [
+    normalizePart(platform, 'platform').toLowerCase(),
+    normalizePart(ownProductId, 'ownProductId'),
+    normalizePart(competitorProductId, 'competitorProductId'),
+  ].join(':');
+}
+
+export function makeReviewKey(input: {
+  platform: string;
+  productId: string;
+  reviewId?: string | null;
+  content: string;
+  reviewDate?: string | null;
+}): string {
+  const prefix = `${normalizePart(input.platform, 'platform').toLowerCase()}:${normalizePart(input.productId, 'productId')}`;
+  if (input.reviewId?.trim()) return `${prefix}:${input.reviewId.trim()}`;
+
+  const fallback = JSON.stringify({
+    content: input.content.trim(),
+    reviewDate: input.reviewDate?.trim() || '',
+  });
+  return `${prefix}:sha256:${createHash('sha256').update(fallback).digest('hex')}`;
+}
+
+export function makeSyncIdempotencyKey(input: {
+  workspaceId: string;
+  platform: string;
+  productIds: string[];
+  scopes: string[];
+  bucket: string;
+}): string {
+  const canonical = JSON.stringify({
+    workspaceId: normalizePart(input.workspaceId, 'workspaceId'),
+    platform: normalizePart(input.platform, 'platform').toLowerCase(),
+    productIds: [...new Set(input.productIds.map((value) => value.trim()).filter(Boolean))].sort(),
+    scopes: [...new Set(input.scopes.map((value) => value.trim()).filter(Boolean))].sort(),
+    bucket: normalizePart(input.bucket, 'bucket'),
+  });
+  return createHash('sha256').update(canonical).digest('hex');
+}
+

+ 62 - 0
src/modules/domestic-voc/jobs/sync-worker.ts

@@ -0,0 +1,62 @@
+import type { Pool } from 'pg';
+
+export interface ClaimedSyncJob {
+  internalId: string;
+  publicId: string;
+  workspaceId: string;
+  platform: string;
+  scopes: string[];
+  productIds: string[];
+  attempts: number;
+}
+
+interface ClaimedSyncJobRow {
+  id: string;
+  public_id: string;
+  workspace_public_id: string;
+  platform: string;
+  scopes: string[];
+  product_ids: string[];
+  attempts: number;
+}
+
+export async function claimNextSyncJob(pool: Pool, workerId: string): Promise<ClaimedSyncJob | null> {
+  const result = await pool.query<ClaimedSyncJobRow>(`
+    WITH next_job AS (
+      SELECT id
+      FROM voc.sync_job
+      WHERE status = 'pending' AND attempts < max_attempts
+      ORDER BY requested_at, id
+      LIMIT 1
+      FOR UPDATE SKIP LOCKED
+    )
+    UPDATE voc.sync_job job
+    SET status = 'processing',
+        worker_id = $1,
+        attempts = attempts + 1,
+        started_at = COALESCE(started_at, now()),
+        updated_at = now()
+    FROM next_job, voc.workspace workspace
+    WHERE job.id = next_job.id AND workspace.id = job.workspace_id
+    RETURNING
+      job.id,
+      job.public_id,
+      workspace.public_id AS workspace_public_id,
+      job.platform,
+      job.scopes,
+      job.product_ids,
+      job.attempts
+  `, [workerId]);
+  const row = result.rows[0];
+  if (!row) return null;
+  return {
+    internalId: row.id,
+    publicId: row.public_id,
+    workspaceId: row.workspace_public_id,
+    platform: row.platform,
+    scopes: row.scopes,
+    productIds: row.product_ids,
+    attempts: row.attempts,
+  };
+}
+

+ 136 - 0
src/modules/domestic-voc/repositories/sync-job.repository.ts

@@ -0,0 +1,136 @@
+import type { Queryable } from '../../../db/types.js';
+
+export interface SyncJobRecord {
+  id: string;
+  workspaceId: string;
+  platform: string;
+  status: string;
+  scopes: string[];
+  productIds: string[];
+  progress: number;
+  attempts: number;
+  maxAttempts: number;
+  errorSummary: string | null;
+  requestedAt: string;
+  startedAt: string | null;
+  completedAt: string | null;
+}
+
+interface SyncJobRow {
+  public_id: string;
+  workspace_public_id: string;
+  platform: string;
+  status: string;
+  scopes: string[];
+  product_ids: string[];
+  progress: number;
+  attempts: number;
+  max_attempts: number;
+  error_summary: string | null;
+  requested_at: Date | string;
+  started_at: Date | string | null;
+  completed_at: Date | string | null;
+}
+
+export class SyncJobRepository {
+  constructor(private readonly database: Queryable) {}
+
+  async enqueue(input: {
+    publicId: string;
+    workspaceId: string;
+    platform: string;
+    idempotencyKey: string;
+    scopes: string[];
+    productIds: string[];
+  }): Promise<SyncJobRecord | null> {
+    const result = await this.database.query<SyncJobRow>(`
+      WITH target_workspace AS (
+        SELECT id, public_id FROM voc.workspace WHERE public_id = $1 AND status = 'active'
+      ), inserted AS (
+        INSERT INTO voc.sync_job (
+          public_id, workspace_id, platform, idempotency_key, scopes, product_ids
+        )
+        SELECT $2, id, $3, $4, $5::jsonb, $6::jsonb
+        FROM target_workspace
+        ON CONFLICT (workspace_id, idempotency_key) DO NOTHING
+        RETURNING *
+      ), selected_job AS (
+        SELECT * FROM inserted
+        UNION ALL
+        SELECT job.*
+        FROM voc.sync_job job
+        JOIN target_workspace workspace ON workspace.id = job.workspace_id
+        WHERE job.idempotency_key = $4
+        LIMIT 1
+      )
+      SELECT
+        job.public_id,
+        workspace.public_id AS workspace_public_id,
+        job.platform,
+        job.status,
+        job.scopes,
+        job.product_ids,
+        job.progress,
+        job.attempts,
+        job.max_attempts,
+        job.error_summary,
+        job.requested_at,
+        job.started_at,
+        job.completed_at
+      FROM selected_job job
+      JOIN voc.workspace workspace ON workspace.id = job.workspace_id
+    `, [
+      input.workspaceId,
+      input.publicId,
+      input.platform,
+      input.idempotencyKey,
+      JSON.stringify(input.scopes),
+      JSON.stringify(input.productIds),
+    ]);
+    const row = result.rows[0];
+    return row ? this.map(row) : null;
+  }
+
+  async findByPublicId(publicId: string): Promise<SyncJobRecord | null> {
+    const result = await this.database.query<SyncJobRow>(`
+      SELECT
+        job.public_id,
+        workspace.public_id AS workspace_public_id,
+        job.platform,
+        job.status,
+        job.scopes,
+        job.product_ids,
+        job.progress,
+        job.attempts,
+        job.max_attempts,
+        job.error_summary,
+        job.requested_at,
+        job.started_at,
+        job.completed_at
+      FROM voc.sync_job job
+      JOIN voc.workspace workspace ON workspace.id = job.workspace_id
+      WHERE job.public_id = $1
+    `, [publicId]);
+    const row = result.rows[0];
+    return row ? this.map(row) : null;
+  }
+
+  private map(row: SyncJobRow): SyncJobRecord {
+    return {
+      id: row.public_id,
+      workspaceId: row.workspace_public_id,
+      platform: row.platform,
+      status: row.status,
+      scopes: row.scopes,
+      productIds: row.product_ids,
+      progress: row.progress,
+      attempts: row.attempts,
+      maxAttempts: row.max_attempts,
+      errorSummary: row.error_summary,
+      requestedAt: new Date(row.requested_at).toISOString(),
+      startedAt: row.started_at ? new Date(row.started_at).toISOString() : null,
+      completedAt: row.completed_at ? new Date(row.completed_at).toISOString() : null,
+    };
+  }
+}
+

+ 77 - 0
src/modules/domestic-voc/routes.ts

@@ -0,0 +1,77 @@
+import { Router } from 'express';
+import { z } from 'zod';
+import type { SyncJobRepository } from './repositories/sync-job.repository.js';
+import type { SnapshotService } from './services/snapshot.service.js';
+import type { SyncService } from './services/sync.service.js';
+
+const syncRequestSchema = z.object({
+  workspaceId: z.string().min(1).default('demashi'),
+  platform: z.literal('jd').default('jd'),
+  productIds: z.array(z.string().min(1)).min(1).max(100),
+  scopes: z.array(z.enum(['product', 'reviews'])).min(1).default(['product', 'reviews']),
+  idempotencyKey: z.string().min(8).max(200).optional(),
+});
+
+const snapshotQuerySchema = z.object({
+  workspaceId: z.string().min(1).default('demashi'),
+  platform: z.literal('jd').default('jd'),
+});
+
+export function createDomesticVocRouter(dependencies: {
+  jobs: SyncJobRepository;
+  sync: SyncService;
+  snapshot: SnapshotService;
+}): Router {
+  const router = Router();
+
+  router.post('/sync', async (request, response, next) => {
+    try {
+      const input = syncRequestSchema.parse(request.body);
+      const headerIdempotencyKey = request.header('Idempotency-Key')?.trim();
+      const { idempotencyKey: bodyIdempotencyKey, ...requiredInput } = input;
+      const idempotencyKey = headerIdempotencyKey || bodyIdempotencyKey;
+      const syncInput = {
+        ...requiredInput,
+        ...(idempotencyKey ? { idempotencyKey } : {}),
+      };
+      const job = await dependencies.sync.enqueue(syncInput);
+      if (!job) {
+        response.status(404).json({ error: 'workspace_not_found' });
+        return;
+      }
+      response.status(202).json({ job });
+    } catch (error) {
+      next(error);
+    }
+  });
+
+  router.get('/jobs/:id', async (request, response, next) => {
+    try {
+      const id = z.uuid().parse(request.params.id);
+      const job = await dependencies.jobs.findByPublicId(id);
+      if (!job) {
+        response.status(404).json({ error: 'job_not_found' });
+        return;
+      }
+      response.json({ job });
+    } catch (error) {
+      next(error);
+    }
+  });
+
+  router.get('/snapshot', async (request, response, next) => {
+    try {
+      const query = snapshotQuerySchema.parse(request.query);
+      const snapshot = await dependencies.snapshot.getSnapshot(query.workspaceId, query.platform);
+      if (!snapshot) {
+        response.status(404).json({ error: 'workspace_not_found' });
+        return;
+      }
+      response.json(snapshot);
+    } catch (error) {
+      next(error);
+    }
+  });
+
+  return router;
+}

+ 394 - 0
src/modules/domestic-voc/services/dataset-import.service.ts

@@ -0,0 +1,394 @@
+import { randomUUID } from 'node:crypto';
+import type { Pool, PoolClient } from 'pg';
+import { makeReviewKey } from '../domain/identity.js';
+import type { DomesticDataset, DomesticProductRelation, DomesticReview } from '../../../types/domestic-dataset.js';
+
+interface ImportDailyMetric {
+  date: string;
+  gmv?: number;
+  soldUnits?: number;
+  transactionOrders?: number;
+  transactionCustomers?: number;
+  impressions?: number;
+  clicks?: number;
+  views?: number;
+  visitors?: number;
+  cartUnits?: number;
+  orderAmount?: number;
+  orderUnits?: number;
+  orderCount?: number;
+  refundAmount?: number;
+  refundUnits?: number;
+  refundOrders?: number;
+}
+
+export interface ImportDataset extends Omit<DomesticDataset, 'products' | 'reviews'> {
+  products: Array<Omit<DomesticDataset['products'][number], 'trend'> & { trend: ImportDailyMetric[] }>;
+  reviews?: DomesticReview[];
+}
+
+interface ProductImportRecord {
+  platform: string;
+  productId: string;
+  productKey: string;
+  role: 'own' | 'competitor';
+  brand: string;
+  title: string;
+  model: string;
+  category1: string;
+  category2: string;
+  category3: string;
+  source: string;
+}
+
+interface MetricImportRecord extends ImportDailyMetric {
+  platform: string;
+  productId: string;
+  source: string;
+}
+
+export interface ExtractedImportRecords {
+  products: ProductImportRecord[];
+  metrics: MetricImportRecord[];
+  relations: DomesticProductRelation[];
+  reviews: DomesticReview[];
+}
+
+export interface DatasetImportResult {
+  batchId: string;
+  products: number;
+  metrics: number;
+  relations: number;
+  reviews: number;
+}
+
+const BATCH_SIZE = 100;
+
+function requiredString(value: unknown, field: string): string {
+  if (typeof value !== 'string' || !value.trim()) throw new Error(`${field} is required`);
+  return value.trim();
+}
+
+function finiteNumber(value: unknown): number {
+  const parsed = Number(value ?? 0);
+  return Number.isFinite(parsed) ? parsed : 0;
+}
+
+function chunks<T>(values: T[], size = BATCH_SIZE): T[][] {
+  const output: T[][] = [];
+  for (let index = 0; index < values.length; index += size) output.push(values.slice(index, index + size));
+  return output;
+}
+
+function valuesClause(rows: unknown[][]): { sql: string; values: unknown[] } {
+  const values: unknown[] = [];
+  const sql = rows.map((row) => {
+    const placeholders = row.map((value) => {
+      values.push(value);
+      return `$${values.length}`;
+    });
+    return `(${placeholders.join(', ')})`;
+  }).join(', ');
+  return { sql, values };
+}
+
+export function extractImportRecords(dataset: ImportDataset): ExtractedImportRecords {
+  const platform = requiredString(dataset.platform, 'platform').toLowerCase();
+  if (!Array.isArray(dataset.products)) throw new Error('products must be an array');
+  if (!Array.isArray(dataset.relations)) throw new Error('relations must be an array');
+
+  const products = dataset.products.map((product, index): ProductImportRecord => ({
+    platform,
+    productId: requiredString(product.productId, `products[${index}].productId`),
+    productKey: requiredString(product.productKey, `products[${index}].productKey`),
+    role: product.role,
+    brand: product.brand || '',
+    title: product.title || '',
+    model: product.model || '',
+    category1: product.category1 || '',
+    category2: product.category2 || '',
+    category3: product.category3 || '',
+    source: product.source || 'dataset-import',
+  }));
+  const productById = new Map(products.map((product) => [product.productId, product]));
+  const mappingByOwnId = new Map(dataset.mappingGroups.map((group) => [group.ownProductId, group]));
+  for (const relation of dataset.relations) {
+    if (!productById.has(relation.ownProductId)) {
+      const mapping = mappingByOwnId.get(relation.ownProductId);
+      const product: ProductImportRecord = {
+        platform,
+        productId: requiredString(relation.ownProductId, 'relation.ownProductId'),
+        productKey: requiredString(relation.ownProductKey, 'relation.ownProductKey'),
+        role: 'own',
+        brand: dataset.caseName || '',
+        title: '',
+        model: mapping?.model || '',
+        category1: '',
+        category2: '',
+        category3: mapping?.category || relation.category || '',
+        source: 'relation_stub',
+      };
+      productById.set(product.productId, product);
+      products.push(product);
+    }
+    if (!productById.has(relation.competitorProductId)) {
+      const product: ProductImportRecord = {
+        platform,
+        productId: requiredString(relation.competitorProductId, 'relation.competitorProductId'),
+        productKey: requiredString(relation.competitorProductKey, 'relation.competitorProductKey'),
+        role: 'competitor',
+        brand: relation.competitorBrand || '',
+        title: '',
+        model: '',
+        category1: '',
+        category2: '',
+        category3: relation.category || '',
+        source: 'relation_stub',
+      };
+      productById.set(product.productId, product);
+      products.push(product);
+    }
+  }
+
+  const metrics = dataset.products.flatMap((product, productIndex) => {
+    if (!Array.isArray(product.trend)) throw new Error(`products[${productIndex}].trend must be an array`);
+    return product.trend.map((metric, metricIndex): MetricImportRecord => {
+      const date = requiredString(metric.date, `products[${productIndex}].trend[${metricIndex}].date`);
+      if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error(`Invalid metric date: ${date}`);
+      return {
+        platform,
+        productId: product.productId,
+        source: product.source || 'dataset-import',
+        date,
+        gmv: finiteNumber(metric.gmv),
+        soldUnits: finiteNumber(metric.soldUnits),
+        transactionOrders: finiteNumber(metric.transactionOrders),
+        transactionCustomers: finiteNumber(metric.transactionCustomers),
+        impressions: finiteNumber(metric.impressions),
+        clicks: finiteNumber(metric.clicks),
+        views: finiteNumber(metric.views),
+        visitors: finiteNumber(metric.visitors),
+        cartUnits: finiteNumber(metric.cartUnits),
+        orderAmount: finiteNumber(metric.orderAmount),
+        orderUnits: finiteNumber(metric.orderUnits),
+        orderCount: finiteNumber(metric.orderCount),
+        refundAmount: finiteNumber(metric.refundAmount),
+        refundUnits: finiteNumber(metric.refundUnits),
+        refundOrders: finiteNumber(metric.refundOrders),
+      };
+    });
+  });
+
+  return {
+    products,
+    metrics,
+    relations: dataset.relations,
+    reviews: dataset.reviews ?? [],
+  };
+}
+
+export class DatasetImportService {
+  constructor(private readonly pool: Pool) {}
+
+  async import(dataset: ImportDataset, workspacePublicId = 'demashi'): Promise<DatasetImportResult> {
+    const records = extractImportRecords(dataset);
+    const batchId = randomUUID();
+    const workspace = await this.pool.query<{ id: string }>(
+      "SELECT id FROM voc.workspace WHERE public_id = $1 AND status = 'active'",
+      [workspacePublicId],
+    );
+    const workspaceId = workspace.rows[0]?.id;
+    if (!workspaceId) throw new Error(`Workspace not found: ${workspacePublicId}`);
+
+    await this.pool.query(`
+      INSERT INTO voc.import_batch (
+        public_id, workspace_id, platform, source_kind, source_file, source_hash,
+        sheet_names, status, total_rows, started_at
+      ) VALUES ($1, $2, $3, 'normalized_dataset', $4, $5, $6::jsonb, 'processing', $7, now())
+    `, [
+      batchId,
+      workspaceId,
+      dataset.platform,
+      dataset.source.sourceFile,
+      dataset.source.sourceHash,
+      JSON.stringify(dataset.source.sheets ?? []),
+      records.products.length + records.metrics.length + records.relations.length + records.reviews.length,
+    ]);
+
+    const client = await this.pool.connect();
+    try {
+      await client.query('BEGIN');
+      const productIds = await this.upsertProducts(client, workspaceId, records.products);
+      await this.upsertMetrics(client, workspaceId, records.metrics, productIds);
+      await this.upsertRelations(client, workspaceId, dataset.platform, records.relations, productIds);
+      await this.upsertReviews(client, workspaceId, dataset.platform, records.reviews, productIds);
+      await client.query(`
+        UPDATE voc.import_batch
+        SET status = 'completed', success_rows = total_rows, completed_at = now()
+        WHERE public_id = $1
+      `, [batchId]);
+      await client.query('COMMIT');
+    } catch (error) {
+      await client.query('ROLLBACK');
+      await this.pool.query(`
+        UPDATE voc.import_batch
+        SET status = 'failed', failed_rows = total_rows, completed_at = now()
+        WHERE public_id = $1
+      `, [batchId]);
+      throw error;
+    } finally {
+      client.release();
+    }
+
+    return {
+      batchId,
+      products: records.products.length,
+      metrics: records.metrics.length,
+      relations: records.relations.length,
+      reviews: records.reviews.length,
+    };
+  }
+
+  private async upsertProducts(
+    client: PoolClient,
+    workspaceId: string,
+    records: ProductImportRecord[],
+  ): Promise<Map<string, string>> {
+    const productIds = new Map<string, string>();
+    for (const batch of chunks(records)) {
+      const values = valuesClause(batch.map((record) => [
+        workspaceId, record.platform, record.productId, record.productKey, record.role,
+        record.brand, record.title, record.model, record.category1, record.category2,
+        record.category3, record.source,
+      ]));
+      const result = await client.query<{ id: string; product_id: string }>(`
+        INSERT INTO voc.product (
+          workspace_id, platform, product_id, product_key, role, brand, title, model,
+          category_1, category_2, category_3, source
+        ) VALUES ${values.sql}
+        ON CONFLICT (workspace_id, platform, product_id) DO UPDATE SET
+          product_key = EXCLUDED.product_key,
+          role = EXCLUDED.role,
+          brand = EXCLUDED.brand,
+          title = EXCLUDED.title,
+          model = EXCLUDED.model,
+          category_1 = EXCLUDED.category_1,
+          category_2 = EXCLUDED.category_2,
+          category_3 = EXCLUDED.category_3,
+          source = EXCLUDED.source,
+          updated_at = now()
+        RETURNING id, product_id
+      `, values.values);
+      for (const row of result.rows) productIds.set(row.product_id, row.id);
+    }
+    return productIds;
+  }
+
+  private async upsertMetrics(
+    client: PoolClient,
+    workspaceId: string,
+    records: MetricImportRecord[],
+    productIds: Map<string, string>,
+  ): Promise<void> {
+    for (const batch of chunks(records)) {
+      const values = valuesClause(batch.map((record) => {
+        const productId = productIds.get(record.productId);
+        if (!productId) throw new Error(`Metric product is missing: ${record.productId}`);
+        return [
+          workspaceId, productId, record.platform, record.date, record.source,
+          record.gmv, record.soldUnits, record.transactionOrders, record.transactionCustomers,
+          record.impressions, record.clicks, record.views, record.visitors, record.cartUnits,
+          record.orderAmount, record.orderUnits, record.orderCount, record.refundAmount,
+          record.refundUnits, record.refundOrders,
+        ];
+      }));
+      await client.query(`
+        INSERT INTO voc.daily_metric (
+          workspace_id, product_id, platform, metric_date, source, gmv, sold_units,
+          transaction_orders, transaction_customers, impressions, clicks, views, visitors,
+          cart_units, order_amount, order_units, order_count, refund_amount, refund_units, refund_orders
+        ) VALUES ${values.sql}
+        ON CONFLICT (workspace_id, platform, product_id, metric_date, source) DO UPDATE SET
+          gmv = EXCLUDED.gmv,
+          sold_units = EXCLUDED.sold_units,
+          transaction_orders = EXCLUDED.transaction_orders,
+          transaction_customers = EXCLUDED.transaction_customers,
+          impressions = EXCLUDED.impressions,
+          clicks = EXCLUDED.clicks,
+          views = EXCLUDED.views,
+          visitors = EXCLUDED.visitors,
+          cart_units = EXCLUDED.cart_units,
+          order_amount = EXCLUDED.order_amount,
+          order_units = EXCLUDED.order_units,
+          order_count = EXCLUDED.order_count,
+          refund_amount = EXCLUDED.refund_amount,
+          refund_units = EXCLUDED.refund_units,
+          refund_orders = EXCLUDED.refund_orders,
+          updated_at = now()
+      `, values.values);
+    }
+  }
+
+  private async upsertRelations(
+    client: PoolClient,
+    workspaceId: string,
+    platform: string,
+    records: DomesticProductRelation[],
+    productIds: Map<string, string>,
+  ): Promise<void> {
+    for (const batch of chunks(records)) {
+      const values = valuesClause(batch.map((record) => {
+        const ownId = productIds.get(record.ownProductId);
+        const competitorId = productIds.get(record.competitorProductId);
+        if (!ownId || !competitorId) throw new Error(`Relation product is missing: ${record.relationKey}`);
+        return [workspaceId, platform, ownId, competitorId, record.relationKey, record.category];
+      }));
+      await client.query(`
+        INSERT INTO voc.product_relation (
+          workspace_id, platform, own_product_id, competitor_product_id, relation_key, category
+        ) VALUES ${values.sql}
+        ON CONFLICT (workspace_id, platform, own_product_id, competitor_product_id) DO UPDATE SET
+          relation_key = EXCLUDED.relation_key,
+          category = EXCLUDED.category,
+          updated_at = now()
+      `, values.values);
+    }
+  }
+
+  private async upsertReviews(
+    client: PoolClient,
+    workspaceId: string,
+    platform: string,
+    records: DomesticReview[],
+    productIds: Map<string, string>,
+  ): Promise<void> {
+    for (const batch of chunks(records)) {
+      const values = valuesClause(batch.map((record) => {
+        const productId = productIds.get(record.productId);
+        if (!productId) throw new Error(`Review product is missing: ${record.productId}`);
+        const reviewKey = makeReviewKey({
+          platform,
+          productId: record.productId,
+          reviewId: record.reviewId,
+          content: record.content,
+          ...(record.reviewDate ? { reviewDate: record.reviewDate } : {}),
+        });
+        return [
+          workspaceId, productId, platform, record.reviewId || null, reviewKey,
+          record.rating, record.content, record.reviewDate || null,
+        ];
+      }));
+      await client.query(`
+        INSERT INTO voc.review (
+          workspace_id, product_id, platform, source_review_id, review_key,
+          rating, content, review_date
+        ) VALUES ${values.sql}
+        ON CONFLICT (workspace_id, platform, review_key) DO UPDATE SET
+          rating = EXCLUDED.rating,
+          content = EXCLUDED.content,
+          review_date = EXCLUDED.review_date,
+          updated_at = now()
+      `, values.values);
+    }
+  }
+}

+ 338 - 0
src/modules/domestic-voc/services/snapshot.service.ts

@@ -0,0 +1,338 @@
+import type { Queryable } from '../../../db/types.js';
+import type {
+  DomesticDataset,
+  DomesticMetricSummary,
+  DomesticProduct,
+  DomesticProductRelation,
+} from '../../../types/domestic-dataset.js';
+
+interface WorkspaceRow {
+  id: string;
+  public_id: string;
+  case_name: string;
+}
+
+interface ProductRow {
+  id: string;
+  platform: string;
+  product_id: string;
+  product_key: string;
+  role: 'own' | 'competitor';
+  brand: string;
+  title: string;
+  model: string;
+  category_1: string;
+  category_2: string;
+  category_3: string;
+  source: string;
+}
+
+interface MetricRow {
+  product_internal_id: string;
+  metric_date: string | Date;
+  gmv: string | number;
+  sold_units: string | number;
+  transaction_orders: string | number;
+  transaction_customers: string | number;
+  impressions: string | number;
+  clicks: string | number;
+  views: string | number;
+  visitors: string | number;
+  cart_units: string | number;
+  order_amount: string | number;
+  order_units: string | number;
+  order_count: string | number;
+  refund_amount: string | number;
+  refund_units: string | number;
+  refund_orders: string | number;
+}
+
+interface RelationRow {
+  relation_key: string;
+  own_internal_id: string;
+  own_product_id: string;
+  own_product_key: string;
+  competitor_internal_id: string;
+  competitor_product_id: string;
+  competitor_product_key: string;
+  competitor_brand: string;
+  own_model: string;
+  own_category_1: string;
+  own_category_2: string;
+  own_category_3: string;
+  category: string;
+}
+
+interface ReviewRow {
+  product_id: string;
+  source_review_id: string | null;
+  review_key: string;
+  rating: string | number | null;
+  content: string;
+  review_date: Date | string | null;
+}
+
+interface BatchRow {
+  source_file: string | null;
+  source_hash: string | null;
+  sheet_names: unknown;
+}
+
+interface MutableSummary extends DomesticMetricSummary {}
+
+function emptySummary(): MutableSummary {
+  return {
+    gmv: 0,
+    soldUnits: 0,
+    transactionOrders: 0,
+    transactionCustomers: 0,
+    impressions: 0,
+    clicks: 0,
+    views: 0,
+    visitors: 0,
+    cartUnits: 0,
+    orderAmount: 0,
+    orderUnits: 0,
+    orderCount: 0,
+    refundAmount: 0,
+    refundUnits: 0,
+    refundOrders: 0,
+    conversionRate: 0,
+    clickThroughRate: 0,
+    averageUnitPrice: 0,
+    refundToGmvRate: 0,
+  };
+}
+
+function numberValue(value: string | number | null | undefined): number {
+  const parsed = Number(value ?? 0);
+  return Number.isFinite(parsed) ? parsed : 0;
+}
+
+function addMetric(summary: MutableSummary, row: MetricRow): void {
+  summary.gmv += numberValue(row.gmv);
+  summary.soldUnits += numberValue(row.sold_units);
+  summary.transactionOrders += numberValue(row.transaction_orders);
+  summary.transactionCustomers += numberValue(row.transaction_customers);
+  summary.impressions += numberValue(row.impressions);
+  summary.clicks += numberValue(row.clicks);
+  summary.views += numberValue(row.views);
+  summary.visitors += numberValue(row.visitors);
+  summary.cartUnits += numberValue(row.cart_units);
+  summary.orderAmount += numberValue(row.order_amount);
+  summary.orderUnits += numberValue(row.order_units);
+  summary.orderCount += numberValue(row.order_count);
+  summary.refundAmount += numberValue(row.refund_amount);
+  summary.refundUnits += numberValue(row.refund_units);
+  summary.refundOrders += numberValue(row.refund_orders);
+}
+
+function finalizeSummary(summary: MutableSummary): DomesticMetricSummary {
+  return {
+    ...summary,
+    conversionRate: summary.visitors > 0 ? summary.transactionOrders / summary.visitors : 0,
+    clickThroughRate: summary.impressions > 0 ? summary.clicks / summary.impressions : 0,
+    averageUnitPrice: summary.soldUnits > 0 ? summary.gmv / summary.soldUnits : 0,
+    refundToGmvRate: summary.gmv > 0 ? summary.refundAmount / summary.gmv : 0,
+  };
+}
+
+function dateOnly(value: Date | string): string {
+  return value instanceof Date ? value.toISOString().slice(0, 10) : String(value).slice(0, 10);
+}
+
+export class SnapshotService {
+  constructor(
+    private readonly database: Queryable,
+    private readonly now: () => Date = () => new Date(),
+  ) {}
+
+  async getSnapshot(workspacePublicId: string, platform: string): Promise<DomesticDataset | null> {
+    const workspaceResult = await this.database.query<WorkspaceRow>(`
+      SELECT id, public_id, case_name
+      FROM voc.workspace
+      WHERE public_id = $1 AND status = 'active'
+    `, [workspacePublicId]);
+    const workspace = workspaceResult.rows[0];
+    if (!workspace) return null;
+
+    const [productResult, metricResult, relationResult, reviewResult, batchResult] = await Promise.all([
+      this.database.query<ProductRow>(`
+        SELECT id, platform, product_id, product_key, role, brand, title, model,
+               category_1, category_2, category_3, source
+        FROM voc.product
+        WHERE workspace_id = $1 AND platform = $2 AND source <> 'relation_stub'
+        ORDER BY id
+      `, [workspace.id, platform]),
+      this.database.query<MetricRow>(`
+        SELECT product_id AS product_internal_id, metric_date, gmv, sold_units,
+               transaction_orders, transaction_customers, impressions, clicks, views,
+               visitors, cart_units, order_amount, order_units, order_count,
+               refund_amount, refund_units, refund_orders
+        FROM voc.daily_metric
+        WHERE workspace_id = $1 AND platform = $2
+        ORDER BY metric_date, id
+      `, [workspace.id, platform]),
+      this.database.query<RelationRow>(`
+        SELECT relation.relation_key,
+               own.id AS own_internal_id,
+               own.product_id AS own_product_id,
+               own.product_key AS own_product_key,
+               competitor.id AS competitor_internal_id,
+               competitor.product_id AS competitor_product_id,
+               competitor.product_key AS competitor_product_key,
+               competitor.brand AS competitor_brand,
+               own.model AS own_model,
+               own.category_1 AS own_category_1,
+               own.category_2 AS own_category_2,
+               own.category_3 AS own_category_3,
+               relation.category
+        FROM voc.product_relation relation
+        JOIN voc.product own ON own.id = relation.own_product_id
+        JOIN voc.product competitor ON competitor.id = relation.competitor_product_id
+        WHERE relation.workspace_id = $1 AND relation.platform = $2
+        ORDER BY relation.id
+      `, [workspace.id, platform]),
+      this.database.query<ReviewRow>(`
+        SELECT product.product_id, review.source_review_id, review.review_key,
+               review.rating, review.content, review.review_date
+        FROM voc.review review
+        JOIN voc.product product ON product.id = review.product_id
+        WHERE review.workspace_id = $1 AND review.platform = $2
+        ORDER BY review.review_date NULLS LAST, review.id
+      `, [workspace.id, platform]),
+      this.database.query<BatchRow>(`
+        SELECT source_file, source_hash, sheet_names
+        FROM voc.import_batch
+        WHERE workspace_id = $1 AND platform = $2 AND status IN ('completed', 'partial')
+        ORDER BY created_at DESC
+        LIMIT 1
+      `, [workspace.id, platform]),
+    ]);
+
+    const summaryByProduct = new Map<string, MutableSummary>();
+    const trendByProduct = new Map<string, DomesticProduct['trend']>();
+    const dailyByDate = new Map<string, MutableSummary>();
+    for (const metric of metricResult.rows) {
+      const productSummary = summaryByProduct.get(metric.product_internal_id) ?? emptySummary();
+      addMetric(productSummary, metric);
+      summaryByProduct.set(metric.product_internal_id, productSummary);
+
+      const date = dateOnly(metric.metric_date);
+      const trend = trendByProduct.get(metric.product_internal_id) ?? [];
+      trend.push({
+        date,
+        gmv: numberValue(metric.gmv),
+        soldUnits: numberValue(metric.sold_units),
+        transactionOrders: numberValue(metric.transaction_orders),
+      });
+      trendByProduct.set(metric.product_internal_id, trend);
+
+      const dailySummary = dailyByDate.get(date) ?? emptySummary();
+      addMetric(dailySummary, metric);
+      dailyByDate.set(date, dailySummary);
+    }
+
+    const relationCount = new Map<string, number>();
+    const relations: DomesticProductRelation[] = relationResult.rows.map((row) => {
+      relationCount.set(row.own_internal_id, (relationCount.get(row.own_internal_id) ?? 0) + 1);
+      return {
+        relationKey: row.relation_key,
+        ownProductKey: row.own_product_key,
+        ownProductId: row.own_product_id,
+        competitorProductKey: row.competitor_product_key,
+        competitorProductId: row.competitor_product_id,
+        competitorBrand: row.competitor_brand,
+        category: row.category,
+      };
+    });
+
+    const products: DomesticProduct[] = productResult.rows.map((row) => ({
+      platform: row.platform,
+      productId: row.product_id,
+      productKey: row.product_key,
+      asin: row.product_id,
+      role: row.role,
+      brand: row.brand,
+      title: row.title,
+      model: row.model,
+      category1: row.category_1,
+      category2: row.category_2,
+      category3: row.category_3,
+      source: row.source,
+      relationCount: relationCount.get(row.id) ?? 0,
+      summary: finalizeSummary(summaryByProduct.get(row.id) ?? emptySummary()),
+      trend: trendByProduct.get(row.id) ?? [],
+    }));
+    const mappingGroupMap = new Map<string, DomesticDataset['mappingGroups'][number]>();
+    relationResult.rows.forEach((row, index) => {
+      const relation = relations[index];
+      if (!relation) return;
+      const group = mappingGroupMap.get(row.own_product_id) ?? {
+        ownProductId: row.own_product_id,
+        ownProductKey: row.own_product_key,
+        model: row.own_model,
+        category: row.own_category_3 || row.own_category_2 || row.own_category_1 || row.category,
+        competitors: [],
+      };
+      group.competitors.push(relation);
+      mappingGroupMap.set(row.own_product_id, group);
+    });
+    const mappingGroups = [...mappingGroupMap.values()];
+
+    const reviews = reviewResult.rows.map((row) => {
+      const review = {
+        productId: row.product_id,
+        reviewId: row.source_review_id || row.review_key,
+        rating: numberValue(row.rating),
+        content: row.content,
+      };
+      return row.review_date
+        ? { ...review, reviewDate: new Date(row.review_date).toISOString() }
+        : review;
+    });
+
+    const dates = [...dailyByDate.keys()].sort();
+    const batch = batchResult.rows[0];
+    const sheets = Array.isArray(batch?.sheet_names)
+      ? batch.sheet_names.filter((value): value is string => typeof value === 'string')
+      : [];
+    const ownProductIds = new Set(relations.map((relation) => relation.ownProductId));
+    const competitorIds = new Set(relations.map((relation) => relation.competitorProductId));
+    const categories2 = new Set(products.map((product) => product.category2).filter(Boolean));
+    const categories3 = new Set(products.map((product) => product.category3).filter(Boolean));
+
+    return {
+      schemaVersion: 1,
+      generatedAt: this.now().toISOString(),
+      caseName: workspace.case_name,
+      platform,
+      source: {
+        sourceFile: batch?.source_file || 'database',
+        sourceHash: batch?.source_hash || '',
+        sheets,
+        dateRange: { start: dates[0] ?? '', end: dates.at(-1) ?? '' },
+      },
+      summary: {
+        metricRows: metricResult.rows.length,
+        metricProducts: summaryByProduct.size,
+        mappingRows: ownProductIds.size,
+        relations: relations.length,
+        uniqueCompetitorProducts: competitorIds.size,
+        category2Count: categories2.size,
+        category3Count: categories3.size,
+        reviewCount: reviews.length,
+      },
+      dailyTotals: dates.map((date) => ({ date, ...finalizeSummary(dailyByDate.get(date) ?? emptySummary()) })),
+      products,
+      mappingGroups,
+      relations,
+      reviews,
+      quality: {
+        orphanMappings: [],
+        mappingsWithoutCompetitor: [],
+        brandWithoutProductId: [],
+      },
+    };
+  }
+}

+ 36 - 0
src/modules/domestic-voc/services/sync.service.ts

@@ -0,0 +1,36 @@
+import { randomUUID } from 'node:crypto';
+import { makeSyncIdempotencyKey } from '../domain/identity.js';
+import type { SyncJobRecord, SyncJobRepository } from '../repositories/sync-job.repository.js';
+
+export class SyncService {
+  constructor(
+    private readonly jobs: SyncJobRepository,
+    private readonly now: () => Date = () => new Date(),
+  ) {}
+
+  async enqueue(input: {
+    workspaceId: string;
+    platform: string;
+    productIds: string[];
+    scopes: string[];
+    idempotencyKey?: string;
+  }): Promise<SyncJobRecord | null> {
+    const bucket = this.now().toISOString().slice(0, 10);
+    const idempotencyKey = input.idempotencyKey?.trim() || makeSyncIdempotencyKey({
+      workspaceId: input.workspaceId,
+      platform: input.platform,
+      productIds: input.productIds,
+      scopes: input.scopes,
+      bucket,
+    });
+    return this.jobs.enqueue({
+      publicId: randomUUID(),
+      workspaceId: input.workspaceId,
+      platform: input.platform,
+      idempotencyKey,
+      scopes: input.scopes,
+      productIds: input.productIds,
+    });
+  }
+}
+

+ 137 - 0
src/modules/domestic-voc/upstream/fmode-client.ts

@@ -0,0 +1,137 @@
+export interface FmodeClientOptions {
+  baseUrl: string;
+  apiKey: string;
+  timeoutMs: number;
+  retries: number;
+  fetch?: typeof fetch;
+  sleep?: (milliseconds: number) => Promise<void>;
+}
+
+export class FmodeRequestError extends Error {
+  constructor(
+    message: string,
+    public readonly status?: number,
+    public readonly retryable = false,
+  ) {
+    super(message);
+    this.name = 'FmodeRequestError';
+  }
+}
+
+export class FmodeVocEcommerceClient {
+  private readonly fetchImplementation: typeof fetch;
+  private readonly sleep: (milliseconds: number) => Promise<void>;
+  private readonly baseUrl: string;
+
+  constructor(private readonly options: FmodeClientOptions) {
+    this.fetchImplementation = options.fetch ?? globalThis.fetch;
+    this.sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
+    this.baseUrl = options.baseUrl.replace(/\/+$/, '');
+  }
+
+  async request<T>(
+    path: string,
+    init: { method?: 'GET' | 'POST'; params?: Record<string, unknown>; refresh?: boolean } = {},
+  ): Promise<T> {
+    const method = init.method ?? 'GET';
+    let attempt = 0;
+    while (true) {
+      try {
+        const response = await this.performRequest<T>(path, method, init.params ?? {}, init.refresh ?? false);
+        if (this.isRetryableBody(response) && attempt < this.options.retries) {
+          attempt += 1;
+          await this.sleep(200 * attempt);
+          continue;
+        }
+        return response;
+      } catch (error) {
+        const normalized = this.normalizeError(error);
+        if (!normalized.retryable || attempt >= this.options.retries) throw normalized;
+        attempt += 1;
+        await this.sleep(200 * attempt);
+      }
+    }
+  }
+
+  private async performRequest<T>(
+    path: string,
+    method: 'GET' | 'POST',
+    params: Record<string, unknown>,
+    refresh: boolean,
+  ): Promise<T> {
+    const normalizedPath = path.replace(/^\/+/, '');
+    if (!normalizedPath || normalizedPath.includes('..')) {
+      throw new FmodeRequestError('Invalid Fmode proxy path');
+    }
+
+    const url = new URL(`${this.baseUrl}/${normalizedPath}`);
+    const form = new URLSearchParams();
+    const target = method === 'GET' ? url.searchParams : form;
+    for (const [key, value] of Object.entries(params)) {
+      this.appendParameter(target, key, value);
+    }
+    if (refresh) target.set('isRefresh', 'true');
+
+    const controller = new AbortController();
+    const timer = setTimeout(() => controller.abort(), this.options.timeoutMs);
+    try {
+      const requestInit: RequestInit = {
+        method,
+        headers: {
+          Accept: 'application/json',
+          Authorization: `Bearer ${this.options.apiKey}`,
+          ...(method === 'POST' ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}),
+        },
+        signal: controller.signal,
+      };
+      if (method === 'POST') requestInit.body = form.toString();
+      const response = await this.fetchImplementation(url, requestInit);
+      const text = await response.text();
+      const body = this.parseBody(text);
+      if (!response.ok) {
+        throw new FmodeRequestError(
+          `Fmode gateway request failed with status ${response.status}`,
+          response.status,
+          response.status === 408 || response.status === 429 || response.status >= 500,
+        );
+      }
+      return body as T;
+    } finally {
+      clearTimeout(timer);
+    }
+  }
+
+  private appendParameter(target: URLSearchParams, key: string, value: unknown): void {
+    if (value === undefined || value === null) return;
+    if (Array.isArray(value)) {
+      for (const item of value) this.appendParameter(target, key, item);
+      return;
+    }
+    target.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value));
+  }
+
+  private parseBody(text: string): unknown {
+    if (!text) return null;
+    try {
+      return JSON.parse(text) as unknown;
+    } catch {
+      throw new FmodeRequestError('Fmode gateway returned invalid JSON');
+    }
+  }
+
+  private isRetryableBody(body: unknown): boolean {
+    if (!body || typeof body !== 'object') return false;
+    const value = body as { code?: unknown; message?: unknown; data?: { code?: unknown; message?: unknown } };
+    const code = Number(value.code ?? value.data?.code);
+    const message = String(value.message ?? value.data?.message ?? '');
+    return code === 301 || /SEND REQUEST AGAIN|COLLECT FAILED|retry/i.test(message);
+  }
+
+  private normalizeError(error: unknown): FmodeRequestError {
+    if (error instanceof FmodeRequestError) return error;
+    if (error instanceof Error && error.name === 'AbortError') {
+      return new FmodeRequestError('Fmode gateway request timed out', undefined, true);
+    }
+    return new FmodeRequestError('Fmode gateway request failed', undefined, true);
+  }
+}

+ 39 - 0
src/server.ts

@@ -0,0 +1,39 @@
+import 'dotenv/config';
+import { createServer } from 'node:http';
+import type { RequestHandler } from 'express';
+import { createApp } from './app.js';
+import { loadConfig } from './config/env.js';
+import { createParseServer } from './config/parse.js';
+import { createDatabasePool } from './db/pool.js';
+
+async function main(): Promise<void> {
+  const config = loadConfig();
+  const pool = createDatabasePool(config);
+  const parseServer = await createParseServer(config);
+  const app = createApp({
+    config,
+    pool,
+    parseApp: parseServer.app as unknown as RequestHandler,
+  });
+  const server = createServer(app);
+
+  server.listen(config.port, config.host, () => {
+    console.log(`[server] listening on http://${config.host}:${config.port}`);
+  });
+
+  const shutdown = async (signal: string) => {
+    console.log(`[server] received ${signal}; shutting down`);
+    server.close(async () => {
+      await parseServer.handleShutdown();
+      await pool.end();
+      process.exit(0);
+    });
+  };
+  process.once('SIGINT', () => void shutdown('SIGINT'));
+  process.once('SIGTERM', () => void shutdown('SIGTERM'));
+}
+
+main().catch((error) => {
+  console.error('[server] startup failed', error instanceof Error ? error.message : error);
+  process.exitCode = 1;
+});

+ 103 - 0
src/types/domestic-dataset.ts

@@ -0,0 +1,103 @@
+export interface DomesticMetricSummary {
+  gmv: number;
+  soldUnits: number;
+  transactionOrders: number;
+  transactionCustomers: number;
+  impressions: number;
+  clicks: number;
+  views: number;
+  visitors: number;
+  cartUnits: number;
+  orderAmount: number;
+  orderUnits: number;
+  orderCount: number;
+  refundAmount: number;
+  refundUnits: number;
+  refundOrders: number;
+  conversionRate: number;
+  clickThroughRate: number;
+  averageUnitPrice: number;
+  refundToGmvRate: number;
+}
+
+export interface DomesticDailyMetric extends DomesticMetricSummary {
+  date: string;
+}
+
+export interface DomesticProduct {
+  platform: string;
+  productId: string;
+  productKey: string;
+  asin: string;
+  role: 'own' | 'competitor';
+  brand: string;
+  title: string;
+  model: string;
+  category1: string;
+  category2: string;
+  category3: string;
+  source: string;
+  relationCount: number;
+  summary: DomesticMetricSummary;
+  trend: Array<{ date: string; gmv: number; soldUnits: number; transactionOrders: number }>;
+}
+
+export interface DomesticReview {
+  productId: string;
+  reviewId: string;
+  rating: number;
+  content: string;
+  reviewDate?: string;
+}
+
+export interface DomesticProductRelation {
+  relationKey: string;
+  ownProductKey: string;
+  ownProductId: string;
+  competitorProductKey: string;
+  competitorProductId: string;
+  competitorBrand: string;
+  category: string;
+}
+
+export interface DomesticMappingGroup {
+  ownProductId: string;
+  ownProductKey: string;
+  model: string;
+  category: string;
+  competitors: DomesticProductRelation[];
+}
+
+export interface DomesticDataset {
+  schemaVersion: number;
+  generatedAt: string;
+  caseName: string;
+  platform: string;
+  source: {
+    sourceFile: string;
+    sourceHash: string;
+    sheets?: string[];
+    dateRange: { start: string; end: string };
+  };
+  summary: {
+    metricRows: number;
+    metricProducts: number;
+    mappingRows: number;
+    relations: number;
+    uniqueCompetitorProducts: number;
+    category2Count: number;
+    category3Count: number;
+    reviewCount: number;
+  };
+  dailyTotals: DomesticDailyMetric[];
+  products: DomesticProduct[];
+  mappingGroups: DomesticMappingGroup[];
+  relations: DomesticProductRelation[];
+  reviews: DomesticReview[];
+  quality: {
+    orphanMappings: Array<{ ownProductId: string; model: string; category: string }>;
+    mappingsWithoutCompetitor: Array<{ ownProductId: string; model: string; category: string }>;
+    brandWithoutProductId: Array<{ ownProductId: string; slot: number; brand: string }>;
+  };
+}
+

+ 85 - 0
test/app.test.ts

@@ -0,0 +1,85 @@
+import assert from 'node:assert/strict';
+import type { AddressInfo } from 'node:net';
+import test from 'node:test';
+import type { Pool } from 'pg';
+import { createApp } from '../src/app.js';
+import { loadConfig } from '../src/config/env.js';
+
+const config = loadConfig({
+  NODE_ENV: 'test',
+  HOST: '127.0.0.1',
+  PORT: '4400',
+  DATABASE_URL: 'postgres://runtime:password@127.0.0.1:5432/saas_voc',
+  PARSE_APP_ID: 'test-app-id',
+  PARSE_MASTER_KEY: 'a'.repeat(32),
+  PARSE_MAINTENANCE_KEY: 'b'.repeat(32),
+  PARSE_SERVER_URL: 'http://127.0.0.1:4400/parse',
+  FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce',
+  FMODE_API_KEY: 'test-key',
+  CORS_ORIGINS: 'http://127.0.0.1:4300',
+});
+
+function createMockPool(): Pool {
+  return {
+    async query(text: string) {
+      if (text.includes('to_regclass')) {
+        return { rows: [{ schema_ready: 'voc.workspace' }], rowCount: 1 };
+      }
+      if (text.includes('WITH target_workspace')) {
+        return {
+          rows: [{
+            public_id: '11111111-1111-4111-8111-111111111111',
+            workspace_public_id: 'demashi',
+            platform: 'jd',
+            status: 'pending',
+            scopes: ['product', 'reviews'],
+            product_ids: ['11266507445'],
+            progress: 0,
+            attempts: 0,
+            max_attempts: 3,
+            error_summary: null,
+            requested_at: '2026-07-23T00:00:00.000Z',
+            started_at: null,
+            completed_at: null,
+          }],
+          rowCount: 1,
+        };
+      }
+      if (text.includes('FROM voc.workspace')) {
+        return { rows: [{ id: '1', public_id: 'demashi', case_name: 'Demashi' }], rowCount: 1 };
+      }
+      return { rows: [], rowCount: 0 };
+    },
+  } as unknown as Pool;
+}
+
+test('HTTP surface exposes health, empty snapshot, and sync queue contract', async () => {
+  const app = createApp({ config, pool: createMockPool() });
+  const server = await new Promise<ReturnType<typeof app.listen>>((resolve) => {
+    const listening = app.listen(0, '127.0.0.1', () => resolve(listening));
+  });
+
+  try {
+    const address = server.address() as AddressInfo;
+    const baseUrl = `http://127.0.0.1:${address.port}`;
+
+    const health = await fetch(`${baseUrl}/health`);
+    assert.equal(health.status, 200);
+    assert.equal((await health.json() as { database: string }).database, 'ready');
+
+    const snapshot = await fetch(`${baseUrl}/api/domestic-voc/snapshot?workspaceId=demashi&platform=jd`);
+    assert.equal(snapshot.status, 200);
+    assert.deepEqual((await snapshot.json() as { products: unknown[] }).products, []);
+
+    const sync = await fetch(`${baseUrl}/api/domestic-voc/sync`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({ productIds: ['11266507445'] }),
+    });
+    assert.equal(sync.status, 202);
+    assert.equal((await sync.json() as { job: { status: string } }).job.status, 'pending');
+  } finally {
+    await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
+  }
+});
+

+ 76 - 0
test/dataset-import.test.ts

@@ -0,0 +1,76 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import {
+  extractImportRecords,
+  type ImportDataset,
+} from '../src/modules/domestic-voc/services/dataset-import.service.js';
+
+test('dataset import extraction preserves full daily operating metrics', () => {
+  const dataset = {
+    caseName: 'Demashi',
+    platform: 'jd',
+    products: [{
+      platform: 'jd',
+      productId: '1001',
+      productKey: 'jd:1001',
+      role: 'own',
+      brand: 'Demashi',
+      title: 'Product',
+      model: 'Model',
+      category1: 'A',
+      category2: 'B',
+      category3: 'C',
+      source: 'case-file',
+      trend: [{
+        date: '2026-07-23',
+        gmv: 120,
+        soldUnits: 3,
+        transactionOrders: 2,
+        transactionCustomers: 2,
+        impressions: 100,
+        clicks: 20,
+        refundAmount: 10,
+      }],
+    }],
+    mappingGroups: [],
+    relations: [],
+  } as unknown as ImportDataset;
+
+  const records = extractImportRecords(dataset);
+
+  assert.equal(records.products.length, 1);
+  assert.equal(records.metrics.length, 1);
+  assert.equal(records.metrics[0]?.impressions, 100);
+  assert.equal(records.metrics[0]?.clicks, 20);
+  assert.equal(records.metrics[0]?.refundAmount, 10);
+});
+
+test('dataset import creates explicit relation stubs for missing mapping products', () => {
+  const dataset = {
+    caseName: 'Demashi',
+    platform: 'jd',
+    products: [],
+    mappingGroups: [{
+      ownProductId: 'own-1',
+      ownProductKey: 'jd:own-1',
+      model: 'DM-1',
+      category: 'Kitchen',
+      competitors: [],
+    }],
+    relations: [{
+      relationKey: 'jd:own-1:competitor-1',
+      ownProductKey: 'jd:own-1',
+      ownProductId: 'own-1',
+      competitorProductKey: 'jd:competitor-1',
+      competitorProductId: 'competitor-1',
+      competitorBrand: 'Competitor',
+      category: 'Kitchen',
+    }],
+  } as unknown as ImportDataset;
+
+  const records = extractImportRecords(dataset);
+
+  assert.equal(records.products.length, 2);
+  assert.deepEqual(records.products.map((product) => product.role), ['own', 'competitor']);
+  assert.ok(records.products.every((product) => product.source === 'relation_stub'));
+});

+ 50 - 0
test/env.test.ts

@@ -0,0 +1,50 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { EnvironmentConfigurationError, loadConfig } from '../src/config/env.js';
+
+const validEnvironment: NodeJS.ProcessEnv = {
+  NODE_ENV: 'test',
+  HOST: '127.0.0.1',
+  PORT: '4400',
+  DATABASE_URL: 'postgres://user:password@127.0.0.1:5432/saas_voc',
+  MIGRATION_DATABASE_URL: 'postgres://owner:password@127.0.0.1:5432/saas_voc',
+  PARSE_APP_ID: 'test-app-id',
+  PARSE_MASTER_KEY: 'a'.repeat(32),
+  PARSE_MAINTENANCE_KEY: 'b'.repeat(32),
+  PARSE_SERVER_URL: 'http://127.0.0.1:4400/parse',
+  FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce',
+  FMODE_API_KEY: 'test-key',
+  CORS_ORIGINS: 'http://127.0.0.1:4300',
+};
+
+test('loadConfig rejects missing secrets before startup', () => {
+  const environment = { ...validEnvironment };
+  delete environment.DATABASE_URL;
+  delete environment.PARSE_MASTER_KEY;
+  delete environment.PARSE_MAINTENANCE_KEY;
+  delete environment.FMODE_API_KEY;
+
+  assert.throws(
+    () => loadConfig(environment),
+    (error: unknown) => {
+      assert.ok(error instanceof EnvironmentConfigurationError);
+      assert.match(error.message, /DATABASE_URL/);
+      assert.match(error.message, /PARSE_MASTER_KEY/);
+      assert.match(error.message, /PARSE_MAINTENANCE_KEY/);
+      assert.match(error.message, /FMODE_API_KEY/);
+      return true;
+    },
+  );
+});
+
+test('loadConfig normalizes URLs and CORS origins', () => {
+  const config = loadConfig({
+    ...validEnvironment,
+    FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce/',
+    CORS_ORIGINS: 'http://127.0.0.1:4300, http://localhost:4200',
+  });
+
+  assert.equal(config.fmode.baseUrl, 'http://127.0.0.1:3000/api/voc-e-commerce');
+  assert.equal(config.database.migrationUrl, validEnvironment.MIGRATION_DATABASE_URL);
+  assert.deepEqual(config.corsOrigins, ['http://127.0.0.1:4300', 'http://localhost:4200']);
+});

+ 74 - 0
test/fmode-client.test.ts

@@ -0,0 +1,74 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { FmodeRequestError, FmodeVocEcommerceClient } from '../src/modules/domestic-voc/upstream/fmode-client.js';
+
+test('Fmode client sends credentials in a header and never in the URL', async () => {
+  const calls: Array<{ url: string; authorization: string | null }> = [];
+  const client = new FmodeVocEcommerceClient({
+    baseUrl: 'http://127.0.0.1:3000/api/voc-e-commerce',
+    apiKey: 'private-test-key',
+    timeoutMs: 1_000,
+    retries: 0,
+    fetch: async (input, init) => {
+      calls.push({
+        url: String(input),
+        authorization: new Headers(init?.headers).get('authorization'),
+      });
+      return new Response(JSON.stringify({ code: 200, data: { items: [] } }), { status: 200 });
+    },
+  });
+
+  await client.request('jd/search-item-list/v1', { params: { keyword: 'test' } });
+
+  assert.equal(calls.length, 1);
+  assert.equal(calls[0]?.authorization, 'Bearer private-test-key');
+  assert.doesNotMatch(calls[0]?.url ?? '', /private-test-key/);
+  assert.match(calls[0]?.url ?? '', /keyword=test/);
+});
+
+test('Fmode client retries transient gateway failures', async () => {
+  let attempts = 0;
+  const client = new FmodeVocEcommerceClient({
+    baseUrl: 'http://127.0.0.1:3000/api/voc-e-commerce',
+    apiKey: 'test-key',
+    timeoutMs: 1_000,
+    retries: 2,
+    sleep: async () => undefined,
+    fetch: async () => {
+      attempts += 1;
+      if (attempts === 1) return new Response('{}', { status: 503 });
+      return new Response(JSON.stringify({ code: 200, data: { ok: true } }), { status: 200 });
+    },
+  });
+
+  const response = await client.request<{ data: { ok: boolean } }>('jd/product-detail/v1');
+  assert.equal(attempts, 2);
+  assert.equal(response.data.ok, true);
+});
+
+test('Fmode client aborts timed out requests without leaking the key', async () => {
+  const client = new FmodeVocEcommerceClient({
+    baseUrl: 'http://127.0.0.1:3000/api/voc-e-commerce',
+    apiKey: 'secret-value-that-must-not-appear',
+    timeoutMs: 5,
+    retries: 0,
+    fetch: async (_input, init) => new Promise((_resolve, reject) => {
+      init?.signal?.addEventListener('abort', () => {
+        const error = new Error('aborted');
+        error.name = 'AbortError';
+        reject(error);
+      });
+    }),
+  });
+
+  await assert.rejects(
+    () => client.request('jd/product-detail/v1'),
+    (error: unknown) => {
+      assert.ok(error instanceof FmodeRequestError);
+      assert.match(error.message, /timed out/);
+      assert.doesNotMatch(error.message, /secret-value/);
+      return true;
+    },
+  );
+});
+

+ 46 - 0
test/identity.test.ts

@@ -0,0 +1,46 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import {
+  makeProductKey,
+  makeRelationKey,
+  makeReviewKey,
+  makeSyncIdempotencyKey,
+} from '../src/modules/domestic-voc/domain/identity.js';
+
+test('domestic product and relation keys are stable', () => {
+  assert.equal(makeProductKey('JD', ' 11266507445 '), 'jd:11266507445');
+  assert.equal(makeRelationKey('JD', 'own-1', 'competitor-2'), 'jd:own-1:competitor-2');
+});
+
+test('review key uses source id when available and a stable hash otherwise', () => {
+  const sourceKey = makeReviewKey({
+    platform: 'jd',
+    productId: '1',
+    reviewId: 'review-10',
+    content: 'content',
+  });
+  assert.equal(sourceKey, 'jd:1:review-10');
+
+  const input = { platform: 'jd', productId: '1', content: 'same content', reviewDate: '2026-07-23' };
+  assert.equal(makeReviewKey(input), makeReviewKey(input));
+  assert.match(makeReviewKey(input), /^jd:1:sha256:[a-f0-9]{64}$/);
+});
+
+test('sync idempotency ignores input ordering', () => {
+  const first = makeSyncIdempotencyKey({
+    workspaceId: 'demashi',
+    platform: 'jd',
+    productIds: ['2', '1'],
+    scopes: ['reviews', 'product'],
+    bucket: '2026-07-23',
+  });
+  const second = makeSyncIdempotencyKey({
+    workspaceId: 'demashi',
+    platform: 'JD',
+    productIds: ['1', '2', '1'],
+    scopes: ['product', 'reviews'],
+    bucket: '2026-07-23',
+  });
+  assert.equal(first, second);
+});
+

+ 69 - 0
test/snapshot.service.test.ts

@@ -0,0 +1,69 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import type { Queryable } from '../src/db/types.js';
+import { SnapshotService } from '../src/modules/domestic-voc/services/snapshot.service.js';
+
+test('snapshot returns the frontend DomesticDataset contract for an empty workspace', async () => {
+  const database = {
+    async query(text: string) {
+      if (text.includes('FROM voc.workspace')) {
+        return { rows: [{ id: '1', public_id: 'demashi', case_name: 'Demashi' }], rowCount: 1 };
+      }
+      return { rows: [], rowCount: 0 };
+    },
+  } as unknown as Queryable;
+  const service = new SnapshotService(database, () => new Date('2026-07-23T00:00:00.000Z'));
+
+  const snapshot = await service.getSnapshot('demashi', 'jd');
+
+  assert.ok(snapshot);
+  assert.equal(snapshot.schemaVersion, 1);
+  assert.equal(snapshot.caseName, 'Demashi');
+  assert.equal(snapshot.platform, 'jd');
+  assert.deepEqual(snapshot.products, []);
+  assert.deepEqual(snapshot.reviews, []);
+  assert.deepEqual(snapshot.dailyTotals, []);
+  assert.deepEqual(snapshot.source.dateRange, { start: '', end: '' });
+  assert.equal(snapshot.summary.reviewCount, 0);
+});
+
+test('snapshot exposes relation groups without leaking relation stub products', async () => {
+  const database = {
+    async query(text: string) {
+      if (text.includes('FROM voc.workspace')) {
+        return { rows: [{ id: '1', public_id: 'demashi', case_name: 'Demashi' }], rowCount: 1 };
+      }
+      if (text.includes('FROM voc.product_relation')) {
+        return {
+          rows: [{
+            relation_key: 'jd:own-1:competitor-1',
+            own_internal_id: '10',
+            own_product_id: 'own-1',
+            own_product_key: 'jd:own-1',
+            competitor_internal_id: '11',
+            competitor_product_id: 'competitor-1',
+            competitor_product_key: 'jd:competitor-1',
+            competitor_brand: 'Competitor',
+            own_model: 'DM-1',
+            own_category_1: '',
+            own_category_2: '',
+            own_category_3: 'Kitchen',
+            category: 'Kitchen',
+          }],
+          rowCount: 1,
+        };
+      }
+      return { rows: [], rowCount: 0 };
+    },
+  } as unknown as Queryable;
+  const service = new SnapshotService(database);
+
+  const snapshot = await service.getSnapshot('demashi', 'jd');
+
+  assert.ok(snapshot);
+  assert.deepEqual(snapshot.products, []);
+  assert.equal(snapshot.relations.length, 1);
+  assert.equal(snapshot.mappingGroups.length, 1);
+  assert.equal(snapshot.mappingGroups[0]?.model, 'DM-1');
+  assert.equal(snapshot.summary.uniqueCompetitorProducts, 1);
+});

+ 22 - 0
tsconfig.json

@@ -0,0 +1,22 @@
+{
+  "compilerOptions": {
+    "target": "ES2023",
+    "module": "NodeNext",
+    "moduleResolution": "NodeNext",
+    "rootDir": ".",
+    "outDir": "dist",
+    "strict": true,
+    "noUncheckedIndexedAccess": true,
+    "exactOptionalPropertyTypes": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "skipLibCheck": true,
+    "sourceMap": true,
+    "declaration": false,
+    "resolveJsonModule": true,
+    "types": ["node"]
+  },
+  "include": ["src/**/*.ts", "scripts/**/*.ts", "test/**/*.ts"],
+  "exclude": ["dist", "node_modules"]
+}
+