From 6bf069e70dcc7f646073db79e5b5e111fbb7b3db Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 17:39:07 +0100 Subject: [PATCH 01/18] fix: dedupe pharmacy-admin auth, fail-fast pharmacy-api boot, re-check rates freshness, hoist severity order - shared/pharmacy-admin-auth.ts: single createPharmacyAdminAuth() factory (safeCompare-based) used by both server.ts and services/pharmacy-api/server.ts, replacing two independently-written Bearer-token checks that each used non-constant-time !==. - services/pharmacy-api/server.ts now throws at module load when PHARMACY_1_PUBLIC_KEY is unset, matching its sibling services, instead of silently leaving defaultPharmacyApp undefined. - services/bill-audit-api/server.ts re-runs checkRatesFreshness() on an unref'd daily interval (in addition to the existing boot-time check) so a long-running process notices the fair-market rates going stale. - services/drug-interaction-api/logic.ts hoists the severityOrder object literal to a module-level constant instead of reallocating it on every sortPairsBySeverity() call. Closes #1081, #1082, #1080, #1083 --- server.ts | 23 +---- .../__tests__/rates-freshness.test.ts | 85 +++++++++++++++++ services/bill-audit-api/server.ts | 6 +- services/drug-interaction-api/logic.ts | 14 +-- .../__tests__/env-fail-fast.test.ts | 42 +++++++++ services/pharmacy-api/server.ts | 42 ++------- shared/pharmacy-admin-auth.ts | 32 +++++++ tests/pharmacy-admin-auth-consistency.test.ts | 94 +++++++++++++++++++ 8 files changed, 275 insertions(+), 63 deletions(-) create mode 100644 services/bill-audit-api/__tests__/rates-freshness.test.ts create mode 100644 services/pharmacy-api/__tests__/env-fail-fast.test.ts create mode 100644 shared/pharmacy-admin-auth.ts create mode 100644 tests/pharmacy-admin-auth-consistency.test.ts diff --git a/server.ts b/server.ts index ee8a1e5..9e4fe31 100644 --- a/server.ts +++ b/server.ts @@ -27,6 +27,7 @@ import { applySecurityMiddleware } from "./shared/security-middleware.ts"; import { createApiDocsRouter } from "./shared/api-docs.ts"; import { logger } from "./shared/logger.ts"; import { requireApiKey } from "./shared/auth.ts"; +import { createPharmacyAdminAuth } from "./shared/pharmacy-admin-auth.ts"; import { validateTask, getSuspiciousTaskCount } from "./shared/task-validation.ts"; import { BillAuditValidationError, @@ -354,27 +355,7 @@ const PHARMACY_ADMIN_TOKEN = process.env.PHARMACY_ADMIN_TOKEN || CAREGIVER_TOKEN const pharmacyStore = createPharmacyPricingStore(); const recipientsStore = createCareRecipientsStore(); -function requirePharmacyAdmin( - req: express.Request, - res: express.Response, - next: express.NextFunction, -) { - const auth = req.headers.authorization; - if (!auth?.startsWith("Bearer ")) { - res - .status(401) - .setHeader("WWW-Authenticate", "Bearer") - .json({ error: "Missing admin token" }); - return; - } - - if (auth.slice("Bearer ".length) !== PHARMACY_ADMIN_TOKEN) { - res.status(403).json({ error: "Invalid admin token" }); - return; - } - - next(); -} +const requirePharmacyAdmin = createPharmacyAdminAuth(PHARMACY_ADMIN_TOKEN); app.get("/pharmacy/drugs", (_req, res) => { const drugs = pharmacyStore.listDrugs(); diff --git a/services/bill-audit-api/__tests__/rates-freshness.test.ts b/services/bill-audit-api/__tests__/rates-freshness.test.ts new file mode 100644 index 0000000..eec676f --- /dev/null +++ b/services/bill-audit-api/__tests__/rates-freshness.test.ts @@ -0,0 +1,85 @@ +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import type { Server } from "http"; + +/** + * Issue #1080 — checkRatesFreshness() was only invoked once at module load, + * so a long-running process that started before RATES_VALID_UNTIL and kept + * running past it (no restart) never re-checked and never warned. + */ + +vi.mock("dotenv/config", () => ({})); +vi.mock("../../../shared/x402-middleware.ts", () => ({ + applyX402Middleware: vi.fn(), + NETWORK: "stellar:testnet", + OZ_FACILITATOR_URL: "https://example.invalid/facilitator", +})); +vi.mock("../../../shared/logger.ts", () => ({ + logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, +})); + +describe("checkRatesFreshness periodic re-check (Issue #1080)", () => { + let server: Server; + let logger: { warn: ReturnType }; + + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-06-01T00:00:00Z")); + + process.env.BILL_PROVIDER_PUBLIC_KEY = "GPUB123TEST"; + process.env.BILL_AUDIT_API_PORT = "0"; + + ({ logger } = (await import("../../../shared/logger.ts")) as any); + const mod = await import("../server.ts"); + server = mod.server; + }); + + afterAll(() => { + server?.close(); + vi.useRealTimers(); + }); + + it("does not warn at boot while rates are still valid (Existing boot check preserved)", () => { + expect(logger.warn).not.toHaveBeenCalled(); + }); + + it("logs a staleness warning after RATES_VALID_UNTIL elapses, without a restart", () => { + vi.setSystemTime(new Date("2027-01-15T00:00:00Z")); + vi.advanceTimersByTime(24 * 60 * 60 * 1000); + + expect(logger.warn).toHaveBeenCalledWith( + expect.objectContaining({ validUntil: "2026-12-31" }), + expect.stringContaining("stale"), + ); + }); +}); + +describe("checkRatesFreshness interval does not keep the process alive (Issue #1080)", () => { + let server: Server; + let unrefSpy: ReturnType; + + beforeAll(async () => { + vi.resetModules(); + process.env.BILL_PROVIDER_PUBLIC_KEY = "GPUB123TEST"; + process.env.BILL_AUDIT_API_PORT = "0"; + + unrefSpy = vi.fn(); + const realSetInterval = global.setInterval; + vi.spyOn(global, "setInterval").mockImplementation((...args: any[]) => { + const timer = (realSetInterval as any)(...args); + timer.unref = unrefSpy.mockImplementation(() => timer); + return timer; + }); + + const mod = await import("../server.ts"); + server = mod.server; + }); + + afterAll(() => { + server?.close(); + vi.restoreAllMocks(); + }); + + it("calls .unref() on the freshness-check interval", () => { + expect(unrefSpy).toHaveBeenCalled(); + }); +}); diff --git a/services/bill-audit-api/server.ts b/services/bill-audit-api/server.ts index 4f7919b..495e3dc 100644 --- a/services/bill-audit-api/server.ts +++ b/services/bill-audit-api/server.ts @@ -144,8 +144,12 @@ function checkRatesFreshness() { } } -// Check freshness at boot +// Check freshness at boot, then daily — a long-running process that started +// before RATES_VALID_UNTIL would otherwise never notice the rates going stale. +const RATES_FRESHNESS_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; checkRatesFreshness(); +const ratesFreshnessInterval = setInterval(checkRatesFreshness, RATES_FRESHNESS_CHECK_INTERVAL_MS); +ratesFreshnessInterval.unref(); interface BillItem { description: string; cptCode: string; quantity: number; chargedAmount: number; } diff --git a/services/drug-interaction-api/logic.ts b/services/drug-interaction-api/logic.ts index e2ce246..1928092 100644 --- a/services/drug-interaction-api/logic.ts +++ b/services/drug-interaction-api/logic.ts @@ -96,16 +96,16 @@ export const DrugInteractionsQuerySchema = z export type DrugInteractionsQuery = z.infer; -function sortPairsBySeverity(pairs: any[]) { - const severityOrder: Record = { - severe: 0, - moderate: 1, - mild: 2, - }; +const SEVERITY_ORDER: Record = { + severe: 0, + moderate: 1, + mild: 2, +}; +function sortPairsBySeverity(pairs: any[]) { return pairs.sort((left, right) => { const severityDiff = - (severityOrder[left.severity] ?? 3) - (severityOrder[right.severity] ?? 3); + (SEVERITY_ORDER[left.severity] ?? 3) - (SEVERITY_ORDER[right.severity] ?? 3); if (severityDiff !== 0) { return severityDiff; } diff --git a/services/pharmacy-api/__tests__/env-fail-fast.test.ts b/services/pharmacy-api/__tests__/env-fail-fast.test.ts new file mode 100644 index 0000000..0d1f4a4 --- /dev/null +++ b/services/pharmacy-api/__tests__/env-fail-fast.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +/** + * Issue #1082 — pharmacy-api was the only payment-adjacent service that + * didn't fail fast when its required public-key env var was missing; it + * silently produced an undefined defaultPharmacyApp instead. It should now + * throw at module load, consistent with drug-interaction-api and + * pharmacy-payment. + */ + +vi.mock("dotenv/config", () => ({})); +vi.mock("../../../shared/x402-middleware.ts", () => ({ + applyX402Middleware: vi.fn(), + NETWORK: "stellar:testnet", + OZ_FACILITATOR_URL: "https://example.test/x402", +})); + +describe("pharmacy-api fail-fast on missing PHARMACY_1_PUBLIC_KEY (Issue #1082)", () => { + const original = process.env.PHARMACY_1_PUBLIC_KEY; + + beforeEach(() => { + vi.resetModules(); + delete process.env.PHARMACY_1_PUBLIC_KEY; + }); + + afterEach(() => { + process.env.PHARMACY_1_PUBLIC_KEY = original; + }); + + it("throws a clear startup error when PHARMACY_1_PUBLIC_KEY is unset", async () => { + await expect(import("../server.ts")).rejects.toThrow( + "PHARMACY_1_PUBLIC_KEY required in .env", + ); + }); + + it("boots normally and exposes a defined defaultPharmacyApp when the env var is set", async () => { + process.env.PHARMACY_1_PUBLIC_KEY = "GBQTESTPHARMACY1"; + const mod = await import("../server.ts"); + expect(mod.defaultPharmacyApp).toBeDefined(); + expect(mod.defaultPharmacyApp.app).toBeDefined(); + }); +}); diff --git a/services/pharmacy-api/server.ts b/services/pharmacy-api/server.ts index 2e332d8..1496c28 100644 --- a/services/pharmacy-api/server.ts +++ b/services/pharmacy-api/server.ts @@ -19,6 +19,7 @@ import { pathToFileURL } from "url"; import { applyX402Middleware, NETWORK, OZ_FACILITATOR_URL } from "../../shared/x402-middleware.ts"; import { createCorsMiddleware } from "../../shared/cors.ts"; import { applySecurityMiddleware } from "../../shared/security-middleware.ts"; +import { createPharmacyAdminAuth } from "../../shared/pharmacy-admin-auth.ts"; import { logger } from "../../shared/logger.ts"; import { requestContextMiddleware } from "../../shared/request-context.ts"; import { requestLoggerMiddleware } from "../../shared/request-logger.ts"; @@ -44,6 +45,8 @@ import type { const PORT = parseInt(process.env.PHARMACY_API_PORT || "3001"); const PAY_TO = process.env.PHARMACY_1_PUBLIC_KEY; +if (!PAY_TO) throw new Error("PHARMACY_1_PUBLIC_KEY required in .env"); + export interface PharmacyAppOptions { payTo: string; pricingStore?: PharmacyPricingStore; @@ -51,31 +54,6 @@ export interface PharmacyAppOptions { enablePayments?: boolean; } -function createAdminMiddleware(adminToken?: string) { - return (req: express.Request, res: express.Response, next: express.NextFunction) => { - if (!adminToken) { - res.status(503).json({ error: "PHARMACY_ADMIN_TOKEN not configured" }); - return; - } - - const auth = req.headers.authorization; - if (!auth?.startsWith("Bearer ")) { - res - .status(401) - .setHeader("WWW-Authenticate", "Bearer") - .json({ error: "Missing admin token" }); - return; - } - - if (auth.slice("Bearer ".length) !== adminToken) { - res.status(403).json({ error: "Invalid admin token" }); - return; - } - - next(); - }; -} - function sendCrudNotFound(res: express.Response, message: string) { res.status(404).json({ error: message }); } @@ -135,7 +113,7 @@ export function createPharmacyApp(options: PharmacyAppOptions) { }); } - const requireAdmin = createAdminMiddleware(adminToken); + const requireAdmin = createPharmacyAdminAuth(adminToken); app.post("/pharmacy/drugs", requireAdmin, (req, res) => { const parsedBody = DrugRecordSchema.safeParse(req.body); @@ -315,20 +293,16 @@ export function createPharmacyApp(options: PharmacyAppOptions) { }; } -export const defaultPharmacyApp: ReturnType | undefined = PAY_TO - ? createPharmacyApp({ payTo: PAY_TO }) - : undefined; +export const defaultPharmacyApp: ReturnType = createPharmacyApp({ + payTo: PAY_TO, +}); const entrypointUrl = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : ""; if (import.meta.url === entrypointUrl) { - if (!PAY_TO) { - throw new Error("PHARMACY_1_PUBLIC_KEY required in .env"); - } - - const startedApp = defaultPharmacyApp ?? createPharmacyApp({ payTo: PAY_TO }); + const startedApp = defaultPharmacyApp; const server = startedApp.app.listen(PORT, () => { logger.info( { diff --git a/shared/pharmacy-admin-auth.ts b/shared/pharmacy-admin-auth.ts new file mode 100644 index 0000000..fdbae36 --- /dev/null +++ b/shared/pharmacy-admin-auth.ts @@ -0,0 +1,32 @@ +import type { Request, Response, NextFunction } from "express"; +import { safeCompare } from "./auth.ts"; + +/** + * Shared Bearer-token admin check for pharmacy-admin routes. + * Used by both the standalone pharmacy-api service and the unified server + * so a single implementation (and a single safeCompare fix) covers both. + */ +export function createPharmacyAdminAuth(adminToken?: string) { + return (req: Request, res: Response, next: NextFunction) => { + if (!adminToken) { + res.status(503).json({ error: "PHARMACY_ADMIN_TOKEN not configured" }); + return; + } + + const auth = req.headers.authorization; + if (!auth?.startsWith("Bearer ")) { + res + .status(401) + .setHeader("WWW-Authenticate", "Bearer") + .json({ error: "Missing admin token" }); + return; + } + + if (!safeCompare(auth.slice("Bearer ".length), adminToken)) { + res.status(403).json({ error: "Invalid admin token" }); + return; + } + + next(); + }; +} diff --git a/tests/pharmacy-admin-auth-consistency.test.ts b/tests/pharmacy-admin-auth-consistency.test.ts new file mode 100644 index 0000000..baca061 --- /dev/null +++ b/tests/pharmacy-admin-auth-consistency.test.ts @@ -0,0 +1,94 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import request from "supertest"; +import { Keypair } from "@stellar/stellar-sdk"; + +/** + * Issue #1081 — the unified server (server.ts) and the standalone + * pharmacy-api (services/pharmacy-api/server.ts) each hand-wrote their own + * Bearer-token admin check. Both now delegate to the shared + * createPharmacyAdminAuth factory (shared/pharmacy-admin-auth.ts), which + * uses safeCompare instead of !==. This test confirms both entrypoints + * reject an invalid admin token consistently. + */ + +vi.spyOn(Keypair, "fromSecret").mockImplementation(() => { + return { + publicKey: () => "GBQTESTPHARMACY1", + secret: () => "mock-secret", + } as any; +}); + +vi.mock("../shared/x402-middleware.ts", () => ({ + applyX402Middleware: vi.fn(), + NETWORK: "stellar:testnet", + OZ_FACILITATOR_URL: "https://example.test/x402", +})); + +// Mock env vars before importing server.ts to satisfy z.object schema validation +process.env.LLM_API_KEY = "mock-key"; +process.env.AGENT_SECRET_KEY = "S-mock-secret"; +process.env.PHARMACY_1_PUBLIC_KEY = "GBQTESTPHARMACY1"; +process.env.BILL_PROVIDER_PUBLIC_KEY = "GBQTESTPHARMACY2"; +process.env.MPP_SECRET_KEY = "S-mock-secret"; +process.env.CAREGIVER_TOKEN = "mock-token"; +process.env.PHARMACY_ADMIN_TOKEN = "shared-admin-token"; + +// Dynamic imports to ensure env vars are set first +const { app: unifiedApp } = await import("../server.ts"); +const { createPharmacyApp } = await import("../services/pharmacy-api/server.ts"); + +describe("pharmacy admin auth consistency across entrypoints", () => { + let standaloneApp: any; + + beforeEach(() => { + standaloneApp = createPharmacyApp({ + payTo: "GBQTESTPHARMACY1", + adminToken: "shared-admin-token", + enablePayments: false, + }).app; + }); + + it("rejects a missing admin token with 401 on both entrypoints", async () => { + const unifiedRes = await request(unifiedApp) + .post("/pharmacy/drugs") + .send({ name: "test-drug" }); + const standaloneRes = await request(standaloneApp) + .post("/pharmacy/drugs") + .send({ name: "test-drug" }); + + expect(unifiedRes.status).toBe(401); + expect(standaloneRes.status).toBe(401); + expect(unifiedRes.body).toEqual({ error: "Missing admin token" }); + expect(standaloneRes.body).toEqual({ error: "Missing admin token" }); + }); + + it("rejects an invalid admin token with 403 on both entrypoints", async () => { + const unifiedRes = await request(unifiedApp) + .post("/pharmacy/drugs") + .set("Authorization", "Bearer wrong-token") + .send({ name: "test-drug" }); + const standaloneRes = await request(standaloneApp) + .post("/pharmacy/drugs") + .set("Authorization", "Bearer wrong-token") + .send({ name: "test-drug" }); + + expect(unifiedRes.status).toBe(403); + expect(standaloneRes.status).toBe(403); + expect(unifiedRes.body).toEqual({ error: "Invalid admin token" }); + expect(standaloneRes.body).toEqual({ error: "Invalid admin token" }); + }); + + it("accepts the correct admin token on both entrypoints", async () => { + const unifiedRes = await request(unifiedApp) + .post("/pharmacy/drugs") + .set("Authorization", "Bearer shared-admin-token") + .send({ name: "test-drug", displayName: "Test Drug", defaultDosage: "10mg" }); + const standaloneRes = await request(standaloneApp) + .post("/pharmacy/drugs") + .set("Authorization", "Bearer shared-admin-token") + .send({ name: "test-drug-2", displayName: "Test Drug 2", defaultDosage: "10mg" }); + + expect(unifiedRes.status).toBe(201); + expect(standaloneRes.status).toBe(201); + }); +}); From e47c6043bf8124eec03a095573297a3216077978 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 18:21:34 +0100 Subject: [PATCH 02/18] ci: sync lockfiles and fix root typecheck config so npm ci and tsc succeed Root cause of every failing check on PR #1174 (ci, check-env-vars, npm audit, CodeQL JS/TS, Playwright E2E): package-lock.json (root and dashboard) was out of sync with package.json, so `npm ci` failed immediately in every workflow with EUSAGE. - package-lock.json / dashboard/package-lock.json: regenerated via `npm install --package-lock-only` to include @vitest/coverage-v8 and its transitive deps (root) and the dashboard's test toolchain (vitest, jsdom, testing-library) that were missing from the lock file, unblocking `npm ci` everywhere. - tsconfig.json: removed the dead project-reference graph (shared/dashboard), which made bare `npx tsc --noEmit` hard-fail with TS6310 ("Referenced project may not disable emit") before checking a single file. - types/vitest-globals.d.ts: added the missing `vitest/globals` ambient type reference so `vi`/`describe`/`it`/etc. resolve under the root tsconfig, instead of erroring as undefined names. - .github/workflows/ci.yml: added the missing "Install dashboard dependencies" step before the dashboard typecheck/test steps, which otherwise ran without dashboard's node_modules ever being installed. --- .github/workflows/ci.yml | 4 + dashboard/package-lock.json | 2800 ++++++++++++++++++++++++++++++++++- package-lock.json | 470 ++++++ tsconfig.json | 6 +- types/vitest-globals.d.ts | 1 + 5 files changed, 3198 insertions(+), 83 deletions(-) create mode 100644 types/vitest-globals.d.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b79e1fb..80d20ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,10 @@ jobs: - name: Install dependencies run: npm ci --legacy-peer-deps + - name: Install dashboard dependencies + run: npm ci + working-directory: dashboard + - name: Typecheck root run: npx tsc --noEmit diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 8c78813..97168fb 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -20,20 +20,35 @@ "devDependencies": { "@playwright/test": "^1.54.2", "@tailwindcss/postcss": "^4", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.4.0", + "@testing-library/react": "^16.0.0", + "@testing-library/user-event": "^14.5.0", "@types/node": "^20", "@types/pdf-parse": "^1.1.5", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^4.3.0", + "@vitest/coverage-v8": "^3.2.6", "eslint": "^9", "eslint-config-next": "16.2.4", + "jsdom": "^25.0.0", "pdf-parse": "^1.1.1", "tailwindcss": "^4", - "typescript": "5.8.3" + "typescript": "5.8.3", + "vitest": "^3.2.6" }, "engines": { "node": ">=22" } }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -47,6 +62,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@apm-js-collab/code-transformer": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.15.0.tgz", @@ -90,6 +119,27 @@ "module-details-from-path": "^1.0.4" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -214,6 +264,16 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -269,6 +329,38 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", @@ -323,6 +415,131 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", @@ -330,30 +547,472 @@ "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "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/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "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, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "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, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1018,6 +1677,34 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1363,6 +2050,17 @@ "node": ">=14" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { "version": "1.59.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", @@ -1379,6 +2077,13 @@ "node": ">=18" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/plugin-commonjs": { "version": "28.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz", @@ -2595,6 +3300,105 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2606,6 +3410,76 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -3200,55 +4074,235 @@ ], "dev": true, "license": "MIT", - "optional": true, + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.7.tgz", + "integrity": "sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.2.7", + "vitest": "3.2.7" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, - "engines": { - "node": ">=14.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, "node_modules/acorn": { "version": "8.16.0", @@ -3310,6 +4364,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "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==", + "dev": true, + "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", @@ -3503,6 +4567,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3510,6 +4584,35 @@ "dev": true, "license": "MIT" }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", + "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/astring": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", @@ -3529,6 +4632,13 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3651,6 +4761,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3751,6 +4871,23 @@ "node": ">=10.0.0" } }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3768,6 +4905,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/cjs-module-lexer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", @@ -3800,6 +4947,19 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -3856,6 +5016,34 @@ "utrie": "^1.0.2" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -3870,6 +5058,57 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/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==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -3941,6 +5180,23 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3984,6 +5240,26 @@ "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4007,6 +5283,13 @@ "node": ">=0.10.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/dompurify": { "version": "3.4.11", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", @@ -4044,6 +5327,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.380", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.380.tgz", @@ -4071,6 +5361,19 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -4255,6 +5558,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", @@ -4687,6 +6032,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.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", @@ -4838,27 +6193,61 @@ "node": ">=16" } }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, "node_modules/fsevents": { @@ -5203,9 +6592,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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==", "dev": true, "license": "MIT", "dependencies": { @@ -5232,6 +6621,26 @@ "hermes-estree": "0.25.1" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -5246,6 +6655,30 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -5259,6 +6692,19 @@ "node": ">= 6" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5311,6 +6757,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5516,6 +6972,16 @@ "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -5602,6 +7068,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -5769,6 +7242,60 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", @@ -5787,6 +7314,22 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -5826,6 +7369,108 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/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==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -6253,6 +7898,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6262,6 +7914,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -6271,6 +7933,47 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6314,6 +8017,39 @@ "node": ">=8.6" } }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -6535,6 +8271,13 @@ "node": ">=18" } }, + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6724,6 +8467,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -6743,6 +8493,19 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6794,6 +8557,23 @@ "node": "20 || >=22" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pdf-parse": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.4.tgz", @@ -6918,6 +8698,41 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -7014,6 +8829,30 @@ "dev": true, "license": "MIT" }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -7187,6 +9026,13 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7266,6 +9112,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -7493,6 +9359,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sonner": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", @@ -7528,6 +9414,13 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/stackblur-canvas": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", @@ -7550,6 +9443,13 @@ "node": ">=6" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -7564,6 +9464,60 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -7677,6 +9631,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -7687,6 +9684,19 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7700,6 +9710,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -7759,6 +9789,13 @@ "node": ">=12.0.0" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", @@ -7780,6 +9817,139 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -7790,6 +9960,20 @@ "utrie": "^1.0.2" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -7807,19 +9991,69 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "bin": { + "tldts": "bin/cli.js" } }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7833,6 +10067,19 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8133,12 +10380,268 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/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/vite/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "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" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -8253,6 +10756,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -8263,6 +10783,130 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "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/wrap-ansi-cjs/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "dev": true, + "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-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package-lock.json b/package-lock.json index 91a9f3d..f7ecc0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "@types/node-cron": "^3.0.11", "@types/supertest": "^7.2.0", "@vitejs/plugin-react": "^4.3.0", + "@vitest/coverage-v8": "^3.2.6", "concurrently": "^9.2.1", "ioredis-mock": "^8.9.0", "jsdom": "^25.0.0", @@ -92,6 +93,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", @@ -424,6 +439,16 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@cfworker/json-schema": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", @@ -1459,6 +1484,16 @@ "node": ">=18" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2526,6 +2561,17 @@ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@prisma/instrumentation": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-5.22.0.tgz", @@ -3900,6 +3946,40 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.7.tgz", + "integrity": "sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.2.7", + "vitest": "3.2.7" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.6.tgz", @@ -4223,6 +4303,25 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", + "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5051,6 +5150,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5807,6 +5913,13 @@ "node": ">=18" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -6127,6 +6240,73 @@ "ws": "*" } }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", @@ -6573,6 +6753,34 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -8245,6 +8453,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "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", @@ -8258,6 +8482,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -8436,6 +8674,219 @@ "bintrees": "1.0.2" } }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/test-exclude/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/test-exclude/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/test-exclude/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -9170,6 +9621,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/tsconfig.json b/tsconfig.json index 7d57c5d..0a26f26 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,9 +22,5 @@ } }, "include": ["**/*.ts"], - "exclude": ["node_modules", "dist", "dashboard", "shared", "e2e", "scripts"], - "references": [ - { "path": "./shared" }, - { "path": "./dashboard" } - ] + "exclude": ["node_modules", "dist", "dashboard", "shared", "e2e", "scripts"] } diff --git a/types/vitest-globals.d.ts b/types/vitest-globals.d.ts new file mode 100644 index 0000000..9896c47 --- /dev/null +++ b/types/vitest-globals.d.ts @@ -0,0 +1 @@ +/// From e050301745c5110bdd5b72a328db468572527b86 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 18:44:22 +0100 Subject: [PATCH 03/18] fix(scripts): make check-env-vars aware of documentation-only wallet vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check-env-vars.ts flagged 9 vars as unused (STELLAR_RPC_URL, AGENT_PUBLIC_KEY, CAREGIVER_SECRET_KEY, CAREGIVER_PUBLIC_KEY, PHARMACY_{1,2,3}_SECRET_KEY, PHARMACY_3_PUBLIC_KEY, BILL_PROVIDER_SECRET_KEY). These are legitimately never read via process.env — they're the other half of wallet key pairs that `npm run setup` (scripts/setup-wallets.ts) prints for every wallet in WALLET_NAMES, documented in .env.example so operators can paste the full pair, even though only one half of each pair (e.g. a recipient's PUBLIC_KEY) is ever loaded at runtime. Also fixed the script's glob ignore patterns ('node_modules/**' etc.), which only matched at the repo root and crashed with EISDIR once dashboard/node_modules exists, since glob would try to read directories like dashboard/node_modules/decimal.js as files. --- scripts/check-env-vars.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/scripts/check-env-vars.ts b/scripts/check-env-vars.ts index bb3f5fe..02c49fa 100644 --- a/scripts/check-env-vars.ts +++ b/scripts/check-env-vars.ts @@ -11,20 +11,38 @@ interface EnvVar { files: string[]; } +// Wallet key-pair halves that `npm run setup` (scripts/setup-wallets.ts) prints +// for every wallet in WALLET_NAMES, documented here for operators to paste the +// full pair into .env, even though only one half of each pair is ever read via +// process.env at runtime (e.g. recipient wallets only need their PUBLIC_KEY; +// the app never loads their SECRET_KEY, and the agent's own PUBLIC_KEY is +// derived from AGENT_SECRET_KEY rather than read separately). +const DOCUMENTATION_ONLY_VARS = new Set([ + 'STELLAR_RPC_URL', + 'AGENT_PUBLIC_KEY', + 'CAREGIVER_SECRET_KEY', + 'CAREGIVER_PUBLIC_KEY', + 'PHARMACY_1_SECRET_KEY', + 'PHARMACY_2_SECRET_KEY', + 'PHARMACY_3_SECRET_KEY', + 'PHARMACY_3_PUBLIC_KEY', + 'BILL_PROVIDER_SECRET_KEY', +]); + async function extractEnvVarsFromExample(): Promise> { const envExamplePath = path.join(process.cwd(), '.env.example'); const content = await fs.readFile(envExamplePath, 'utf-8'); const lines = content.split('\n'); - + const envVars = new Map(); - + lines.forEach((line, index) => { // Match lines like: VAR_NAME=value or # VAR_NAME=value const match = line.match(/^#?\s*([A-Z_][A-Z0-9_]*)=/); if (match) { const varName = match[1]; - // Skip common meta variables - if (!['NODE_ENV', 'PORT', 'HOST'].includes(varName)) { + // Skip common meta variables and documented-but-intentionally-unread vars + if (!['NODE_ENV', 'PORT', 'HOST'].includes(varName) && !DOCUMENTATION_ONLY_VARS.has(varName)) { envVars.set(varName, { name: varName, line: index + 1, @@ -34,13 +52,13 @@ async function extractEnvVarsFromExample(): Promise> { } } }); - + return envVars; } async function searchCodebaseForEnvVars(envVars: Map): Promise { const files = await glob('**/*.{ts,js,tsx,jsx}', { - ignore: ['node_modules/**', 'dist/**', '.next/**', 'scripts/check-env-vars.ts'], + ignore: ['**/node_modules/**', '**/dist/**', '**/.next/**', 'scripts/check-env-vars.ts'], }); for (const file of files) { From 5ec06ea5328488451a1435f916c5a36f7566ec99 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 18:53:18 +0100 Subject: [PATCH 04/18] fix(ci): use pnpm exec to invoke playwright's install CLI in e2e.yml `pnpm --dir dashboard playwright install --with-deps chromium` failed with ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL because "playwright" is neither a pnpm subcommand nor a package.json script name in dashboard/, so pnpm fell back to its recursive-exec resolution and treated "dashboard" as the command to run. `pnpm exec` is the correct way to invoke a binary from node_modules/.bin (verified locally: the broken form reproduces the exact CI error, `pnpm --dir dashboard exec playwright install` works). --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c0c0396..76ce419 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -47,7 +47,7 @@ jobs: working-directory: dashboard - name: Install Playwright chromium - run: pnpm --dir dashboard playwright install --with-deps chromium + run: pnpm --dir dashboard exec playwright install --with-deps chromium - name: Build dashboard run: npm run build From e5ce41ac5ddc72a3f4dd7b6f4f7f50353fbc9159 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 19:00:07 +0100 Subject: [PATCH 05/18] fix(agent): derive SpendingPolicyInput from z.input, dedupe fs import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SpendingPolicyInput was typed via z.infer (the parsed *output* type), which makes holdTimeSeconds required even though the schema gives it .default(0). Every caller that omits holdTimeSeconds — which setSpendingPolicy is explicitly designed to support via its DEFAULT_POLICY merge — failed to typecheck. Using z.input instead correctly reflects that defaulted fields are optional on input, fixing ~40 spurious TS2345 errors across agent/__tests__ and tests/ with no behavior change. Also removed a duplicate `import { existsSync, mkdirSync } from "fs"` line in agent/server.ts (already imported on the line above). --- agent/server.ts | 1 - agent/tools.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/agent/server.ts b/agent/server.ts index 6b2c5e5..70fa4bd 100644 --- a/agent/server.ts +++ b/agent/server.ts @@ -11,7 +11,6 @@ import "dotenv/config"; import { createHash } from "crypto"; import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs"; -import { existsSync, mkdirSync } from "fs"; import express, { type Express } from "express"; import OpenAI from "openai"; import { Keypair, Horizon } from "@stellar/stellar-sdk"; diff --git a/agent/tools.ts b/agent/tools.ts index cf345a8..9b4ecf0 100644 --- a/agent/tools.ts +++ b/agent/tools.ts @@ -680,7 +680,7 @@ export const SpendingPolicySchema = z.object({ { message: 'medicationMonthlyBudget + billMonthlyBudget cannot exceed monthlyLimit', path: ['medicationMonthlyBudget'] }, ); -type SpendingPolicyInput = z.infer; +type SpendingPolicyInput = z.input; const SPENDING_CACHE_TTL_MS = 5000; /** Compact the JSONL log into a snapshot every this many transactions (Issue #205). */ From 83831471ebd5026e62594daad5ddfa1a5d64308f Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 19:00:59 +0100 Subject: [PATCH 06/18] test(agent): fix stale API references in tools.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getPharmacyPrices -> comparePharmacyPrices (the actual exported name; the old name was never a real export, so the import itself failed to compile). - getSpendingTracker() takes no arguments (recipient scoping happens via the preceding setSpendingPolicy(recipientId, ...) call) — five call sites were still passing a stale 'test-recipient' argument. - getX402Fetch()/extractX402TxHash() are functions local to tools.ts, not exports of the @x402/fetch package, so mocking '@x402/fetch'.getX402Fetch had no effect on the code under test. Replaced with the same wrapFetchWithPayment-stubbing approach already used successfully in agent/__tests__/tools-x402.test.ts, so the malformed-JSON test actually exercises the code path it claims to. --- agent/tools.test.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/agent/tools.test.ts b/agent/tools.test.ts index 10c72e3..647e11a 100644 --- a/agent/tools.test.ts +++ b/agent/tools.test.ts @@ -4,11 +4,10 @@ import { setSpendingPolicy, getSpendingTracker, resetSpendingTracker, - getPharmacyPrices, + comparePharmacyPrices, fetchRosaBill, } from './tools'; import { TRANSACTION_CATEGORY } from '../shared/types'; -import * as x402fetch from '@x402/fetch'; // Mock getLocalDayBounds from tz.ts so we can control time boundaries import * as tz from './tz'; @@ -20,15 +19,23 @@ vi.mock('./tz', async (importOriginal) => { }; }); -// Mock network responses +// getX402Fetch()/extractX402TxHash() are local to tools.ts, not exports of +// @x402/fetch — so the live (non-mock-network) path is controlled here the +// same way agent/__tests__/tools-x402.test.ts does: stub wrapFetchWithPayment +// to hand back one fixed mock fetch function. +const { x402FetchMock } = vi.hoisted(() => ({ x402FetchMock: vi.fn() })); vi.mock('@x402/fetch', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - getX402Fetch: vi.fn(), - extractX402TxHash: vi.fn(() => 'mock-hash'), + wrapFetchWithPayment: vi.fn().mockReturnValue(x402FetchMock), + x402Client: vi.fn().mockReturnValue({ register: vi.fn().mockReturnThis() }), }; }); +vi.mock('@x402/stellar', () => ({ + createEd25519Signer: vi.fn().mockReturnValue({}), + ExactStellarScheme: vi.fn(), +})); describe('Spending Policy Engine', () => { beforeEach(() => { @@ -54,7 +61,7 @@ describe('Spending Policy Engine', () => { dailyLimit: 100, approvalThreshold: 50, }); - const tracker = getSpendingTracker('test-recipient'); + const tracker = getSpendingTracker(); tracker.medications = 50; const result = checkSpendingPolicy(30, TRANSACTION_CATEGORY.MEDICATIONS); @@ -70,7 +77,7 @@ describe('Spending Policy Engine', () => { dailyLimit: 100, approvalThreshold: 50, }); - const tracker = getSpendingTracker('test-recipient'); + const tracker = getSpendingTracker(); tracker.medications = 80; const result = checkSpendingPolicy(30, TRANSACTION_CATEGORY.MEDICATIONS); @@ -86,7 +93,7 @@ describe('Spending Policy Engine', () => { dailyLimit: 100, approvalThreshold: 200, // High threshold }); - const tracker = getSpendingTracker('test-recipient'); + const tracker = getSpendingTracker(); tracker.medications = 0; // Add a transaction today for 80 @@ -132,7 +139,7 @@ describe('Spending Policy Engine', () => { dayEnd: new Date('2026-01-02T05:00:00.000Z'), }); - const tracker = getSpendingTracker('test-recipient'); + const tracker = getSpendingTracker(); // Transaction just BEFORE the day started in NY (yesterday) tracker.transactions.push({ @@ -168,7 +175,7 @@ describe('Spending Policy Engine', () => { approvalThreshold: 500, }); - const tracker = getSpendingTracker('test-recipient'); + const tracker = getSpendingTracker(); // Spent 900 on bills tracker.bills = 900; @@ -191,14 +198,13 @@ describe('Safe JSON Parsing (Issue #161)', () => { }); it('handles malformed JSON from pharmacy API gracefully', async () => { - const mockFetch = vi.fn().mockResolvedValue({ + x402FetchMock.mockResolvedValue({ ok: true, headers: new Headers({ 'payment-response': 'mock-hash' }), json: () => Promise.reject(new SyntaxError('Unexpected end of JSON input')), }); - vi.mocked(x402fetch.getX402Fetch).mockReturnValue(mockFetch); - const result = await getPharmacyPrices('Lisinopril'); + const result = await comparePharmacyPrices('Lisinopril'); expect(result).toEqual({ ok: false, reason: 'MALFORMED_RESPONSE' }); }); From 50d0b2d4760d29cc23845cb1652aaef20a1ab2ef Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 19:39:12 +0100 Subject: [PATCH 07/18] fix(agent): implement documented lazy MPP client cache (issue #196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/runbooks/switch-network.md documents that the MPP client is "lazy-constructed on first use and cached for 60 seconds" so operators can switch STELLAR_NETWORK at runtime via SIGHUP without a full restart — but tools.ts actually constructed mppClient once, eagerly, at module load, with no cache, no TTL, and no invalidateMppClientCache export. This left both the documented runbook procedure and agent/__tests__/mpp-client-lazy.test.ts's 5 tests broken (the test import itself failed to compile). Implemented getMppClient()/invalidateMppClientCache()/setMppClient() following the exact same lazy + 60s TTL + SIGHUP-invalidation pattern already used for getX402Fetch() in this same file, and updated the one call site to go through getMppClient() instead of the old module-level variable. Also fixed the test's env setup: it never disabled MOCK_NETWORK, so getMppClient() always took the mock branch and the spy on the real Mppx.create() path was never exercised. --- agent/__tests__/mpp-client-lazy.test.ts | 3 + agent/tools.ts | 95 +++++++++++++++---------- 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/agent/__tests__/mpp-client-lazy.test.ts b/agent/__tests__/mpp-client-lazy.test.ts index ce41b82..18e424c 100644 --- a/agent/__tests__/mpp-client-lazy.test.ts +++ b/agent/__tests__/mpp-client-lazy.test.ts @@ -4,6 +4,9 @@ const { mppCreateSpy, MOCK_HINT } = vi.hoisted(() => { process.env.AGENT_SECRET_KEY = "SBWWZYCAFDDJXNRRMKSFNRB6OTVZHTCMPUCVZ4FBZLSPHFKHYLPRTJCD"; + // Disable mock network so getMppClient() goes through the real + // createMppClient() -> Mppx.create() path that mppCreateSpy observes. + process.env.MOCK_NETWORK = "0"; const MOCK_HINT = Buffer.from([0xca, 0xfe, 0xba, 0xbe]); const mppCreateSpy = vi.fn().mockReturnValue({ fetch: vi.fn() }); return { mppCreateSpy, MOCK_HINT }; diff --git a/agent/tools.ts b/agent/tools.ts index 9b4ecf0..d23051f 100644 --- a/agent/tools.ts +++ b/agent/tools.ts @@ -478,56 +478,79 @@ process.on('SIGHUP', () => { }); // --- MPP Client: Auto-handles 402 for medication order payments --- -// Use factory function to create client instance (supports DI for testing) -let mppClient: MppClientInstance = isMockNetwork() - ? { - fetch: async (input: RequestInfo | URL, init?: RequestInit) => { - const receipt = createMockReceipt('mpp', { - url: String(input), - body: init?.body ? String(init.body) : '', - }); - return new Response( - JSON.stringify({ - success: true, - order: { id: receipt.receiptId }, - receipt, - }), - { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Payment-Receipt': Buffer.from( - JSON.stringify({ reference: receipt.stellarTxHash }), - ).toString('base64'), - }, +// Lazy-constructed on first use and cached for 60s (issue #196) so +// STELLAR_NETWORK can be switched at runtime via SIGHUP without a full +// process restart. See docs/runbooks/switch-network.md. +function createMockMppClient(): MppClientInstance { + return { + fetch: async (input: RequestInfo | URL, init?: RequestInit) => { + const receipt = createMockReceipt('mpp', { + url: String(input), + body: init?.body ? String(init.body) : '', + }); + return new Response( + JSON.stringify({ + success: true, + order: { id: receipt.receiptId }, + receipt, + }), + { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Payment-Receipt': Buffer.from( + JSON.stringify({ reference: receipt.stellarTxHash }), + ).toString('base64'), }, - ); - }, - get lastTxHash() { - return undefined; - }, - } - : createMppClient({ - keypair: agentKeypair, - mode: 'pull', - }); + }, + ); + }, + get lastTxHash() { + return undefined; + }, + }; +} + +export const MPP_CLIENT_TTL_MS = 60_000; +let _mppClient: MppClientInstance | null = null; +let _mppClientCreatedAt = 0; /** * Set a custom MPP client instance (for testing/DI). * @param client - MPP client instance to use */ export function setMppClient(client: MppClientInstance) { - mppClient = client; + _mppClient = client; + _mppClientCreatedAt = Date.now(); +} + +/** Force the next getMppClient() call to construct a fresh client. */ +export function invalidateMppClientCache() { + _mppClient = null; + _mppClientCreatedAt = 0; } /** - * Get the current MPP client instance. + * Get the current MPP client instance, constructing (or reconstructing) it + * if there is none cached or the 60s TTL has elapsed. * @returns Current MPP client */ export function getMppClient(): MppClientInstance { - return mppClient; + const now = Date.now(); + if (!_mppClient || now - _mppClientCreatedAt > MPP_CLIENT_TTL_MS) { + _mppClient = isMockNetwork() + ? createMockMppClient() + : createMppClient({ keypair: agentKeypair, mode: 'pull' }); + _mppClientCreatedAt = now; + } + return _mppClient; } +process.on('SIGHUP', () => { + invalidateMppClientCache(); + logger.info('[mpp] SIGHUP received — client cache invalidated, will reload on next call'); +}); + // --- Per-recipient data directories (Issue #261) --- const DATA_DIR = process.env.DATA_DIR || fileURLToPath(new URL('../data', import.meta.url)); export function getDataDir(): string { @@ -1743,7 +1766,7 @@ async function executeMedicationPayment( } try { - const response = await mppClient.fetch( + const response = await getMppClient().fetch( `${PHARMACY_PAYMENT_API}/pharmacy/order`, { method: 'POST', From c90fa8c0a22250d56833be792f3c42770a7f6825 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 19:40:23 +0100 Subject: [PATCH 08/18] fix(tests): correct invalid fallback AGENT_SECRET_KEY in tests/setup.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global fallback AGENT_SECRET_KEY ("SAKYNUBM36...") fails Stellar's StrKey checksum validation, so any test file that imports agent/tools.ts without overriding AGENT_SECRET_KEY itself crashed with "invalid checksum" the moment tools.ts called Keypair.fromSecret() at module load — a real, valid-looking key was needed, not just a same-length string. Replaced it with a freshly generated, checksum-valid testnet keypair's secret. --- tests/setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/setup.ts b/tests/setup.ts index 0cbb535..9c12cff 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -5,7 +5,7 @@ import fs from "fs"; process.env.LOG_LEVEL = "silent"; process.env.CAREGIVER_TOKEN = process.env.CAREGIVER_TOKEN || "test-caregiver-token"; -process.env.AGENT_SECRET_KEY = process.env.AGENT_SECRET_KEY || "SAKYNUBM36I4L6H5X2B7QYY46X2F52BNV25SHT2R6S3N7J4D4FMM5XQ6"; +process.env.AGENT_SECRET_KEY = process.env.AGENT_SECRET_KEY || "SAQDKF5AKPWQZRXGHLH523VC7DU46U7IEQNOALOUO2A2UR55NWPVDRGF"; process.env.MOCK_NETWORK = "1"; process.env.STELLAR_NETWORK = "testnet"; process.env.PHARMACY_1_PUBLIC_KEY = process.env.PHARMACY_1_PUBLIC_KEY || "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"; From d04239cd9dbbb25616f5062c69d58f904ba2f504 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 19:41:17 +0100 Subject: [PATCH 09/18] test(agent): set AGENT_API_KEY so auth-rejection assertions are meaningful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit requireApiKey() (shared/auth.ts) only enforces the Bearer token when AGENT_API_KEY is configured (or NODE_ENV=production) — otherwise it calls next() unconditionally. Neither test file set AGENT_API_KEY, so every /agent/* request — with a missing, wrong, or correct token — silently passed through unauthenticated, making the "missing/wrong token returns 401" assertions fail against a real 200/400 response. --- agent/__tests__/agent-endpoints.test.ts | 1 + agent/__tests__/cors-auth.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/agent/__tests__/agent-endpoints.test.ts b/agent/__tests__/agent-endpoints.test.ts index ff31301..15479ad 100644 --- a/agent/__tests__/agent-endpoints.test.ts +++ b/agent/__tests__/agent-endpoints.test.ts @@ -94,6 +94,7 @@ process.env.PHARMACY_1_PUBLIC_KEY = "GBQTESTPHARMACY1PUBKEY"; process.env.BILL_PROVIDER_PUBLIC_KEY = "GBQTESTBILLPROVIDERPUBKEY"; process.env.MPP_SECRET_KEY = "test-mpp-secret-key"; process.env.CAREGIVER_TOKEN = "test-caregiver-token"; +process.env.AGENT_API_KEY = "test-agent-api-key"; const { app } = await import("../../server.ts"); const auth = (req: any) => req.set("Authorization", "Bearer test-agent-api-key"); diff --git a/agent/__tests__/cors-auth.test.ts b/agent/__tests__/cors-auth.test.ts index 50086c2..1f21dd9 100644 --- a/agent/__tests__/cors-auth.test.ts +++ b/agent/__tests__/cors-auth.test.ts @@ -79,6 +79,7 @@ process.env.BILL_PROVIDER_PUBLIC_KEY = "GBQTESTBILLPROVIDERPUBKEY"; process.env.MPP_SECRET_KEY = "test-mpp-secret-key"; process.env.CAREGIVER_TOKEN = "test-caregiver-token"; process.env.ALLOWED_ORIGINS = "http://localhost:3000"; +process.env.AGENT_API_KEY = "test-agent-api-key"; const { app } = await import("../../server.ts"); const auth = (req: any) => req.set("Authorization", "Bearer test-agent-api-key"); From a90a6a9e8e51cdfc00136e20ffabbaa905e23e78 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 20:24:32 +0100 Subject: [PATCH 10/18] test(agent): fix stale/malformed tx-hash fixtures in x402 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract-x402-tx-hash.test.ts: extractX402TxHash() has a three-way return contract (undefined / TX_HASH_EXTRACTION_FAILED sentinel / real hash) — 5 assertions still expected the old two-way (undefined-only) contract for cases where decode fails but the header isn't a valid 64-char hex fallback, so they now get the sentinel instead of undefined. x402-settle-contract.test.ts: all 5 "transaction hash" fixture constants were 60-61 character strings, not real 64-char hex, so every assertion checking `.toMatch(/^[a-f0-9]{64}$/)` or comparing extraction output against them failed. Replaced with genuine 64-char hex values. --- agent/__tests__/extract-x402-tx-hash.test.ts | 22 +++++++++--------- agent/__tests__/x402-settle-contract.test.ts | 24 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/agent/__tests__/extract-x402-tx-hash.test.ts b/agent/__tests__/extract-x402-tx-hash.test.ts index f67a68a..2e7ee42 100644 --- a/agent/__tests__/extract-x402-tx-hash.test.ts +++ b/agent/__tests__/extract-x402-tx-hash.test.ts @@ -28,7 +28,7 @@ vi.mock("@x402/stellar", () => ({ createEd25519Signer: vi.fn(), ExactStellarSche vi.mock("@stellar/mpp/charge/client", () => ({ stellar: vi.fn() })); vi.mock("mppx/client", () => ({ Mppx: { create: vi.fn() } })); -import { extractX402TxHash } from "../tools.ts"; +import { extractX402TxHash, TX_HASH_EXTRACTION_FAILED } from "../tools.ts"; function makeResponse(headerValue: string | null): Response { const headers = new Headers(); @@ -72,16 +72,16 @@ describe("extractX402TxHash", () => { expect(result).toBeUndefined(); }); - it("should return undefined when decoded has no transaction field", () => { + it("should return TX_HASH_EXTRACTION_FAILED when decoded has no transaction field", () => { mockDecode.mockReturnValue({}); const result = extractX402TxHash(makeResponse("dGVzdA==")); - expect(result).toBeUndefined(); + expect(result).toBe(TX_HASH_EXTRACTION_FAILED); }); - it("should return undefined when decode throws and header is not 64-char hex", () => { + it("should return TX_HASH_EXTRACTION_FAILED when decode throws and header is not 64-char hex", () => { mockDecode.mockImplementation(() => { throw new Error("decode failed"); }); const result = extractX402TxHash(makeResponse("short")); - expect(result).toBeUndefined(); + expect(result).toBe(TX_HASH_EXTRACTION_FAILED); }); it("should fall back to raw header when decode throws and header is 64-char hex", () => { @@ -91,21 +91,21 @@ describe("extractX402TxHash", () => { expect(result).toBe(hash); }); - it("should return undefined for 63-char hex header when decode throws", () => { + it("should return TX_HASH_EXTRACTION_FAILED for 63-char hex header when decode throws", () => { mockDecode.mockImplementation(() => { throw new Error("decode failed"); }); const result = extractX402TxHash(makeResponse("a".repeat(63))); - expect(result).toBeUndefined(); + expect(result).toBe(TX_HASH_EXTRACTION_FAILED); }); - it("should return undefined for 65-char hex header when decode throws", () => { + it("should return TX_HASH_EXTRACTION_FAILED for 65-char hex header when decode throws", () => { mockDecode.mockImplementation(() => { throw new Error("decode failed"); }); const result = extractX402TxHash(makeResponse("a".repeat(65))); - expect(result).toBeUndefined(); + expect(result).toBe(TX_HASH_EXTRACTION_FAILED); }); - it("should return undefined on malformed base64 in decode", () => { + it("should return TX_HASH_EXTRACTION_FAILED on malformed base64 in decode", () => { mockDecode.mockImplementation(() => { throw new Error("malformed base64"); }); const result = extractX402TxHash(makeResponse("!!!invalid-base64!!!")); - expect(result).toBeUndefined(); + expect(result).toBe(TX_HASH_EXTRACTION_FAILED); }); }); diff --git a/agent/__tests__/x402-settle-contract.test.ts b/agent/__tests__/x402-settle-contract.test.ts index 5c40eb2..acc6a0d 100644 --- a/agent/__tests__/x402-settle-contract.test.ts +++ b/agent/__tests__/x402-settle-contract.test.ts @@ -33,7 +33,7 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { it("pinned settle-success response is validated", () => { const settleResponse = { status: "success", - transactionHash: "c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8", + transactionHash: "fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02", settlementId: "settle-123", }; @@ -43,11 +43,11 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { it("stellar tx hash is extracted from settle response", () => { const settleResponse = { - paymentResponse: "c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8", + paymentResponse: "fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02", }; const txHash = extractX402TxHash(settleResponse); - expect(txHash).toBe("c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8"); + expect(txHash).toBe("fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02"); }); it("settle response lacking tx hash returns undefined (no fallback)", () => { @@ -73,7 +73,7 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { }); it("extracted hash is valid Stellar transaction hash format", () => { - const validHash = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"; + const validHash = "32eb1a47202c2e84177b5b371f0baa53ab1b8bf6c4e211c467e7cee7d385cc69"; expect(validHash).toMatch(/^[a-f0-9]{64}$/); expect(validHash.length).toBe(64); }); @@ -86,7 +86,7 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { }, expectedResponse: { status: "success", - transactionHash: "c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8", + transactionHash: "fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02", settlementId: "settle-123", }, }; @@ -115,7 +115,7 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { it("settlement confirmed when tx hash present and valid", () => { const settleResponse = { status: "success", - transactionHash: "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b", + transactionHash: "d3e253f80a345c330dd6b663eadb74042f5ad06031502249e3d58a1364091c4a", }; const txHash = extractX402TxHash({ paymentResponse: settleResponse.transactionHash }); @@ -136,29 +136,29 @@ describe("x402 Facilitator settle — contract (Issue #814)", () => { it("PAYMENT-RESPONSE header variant is recognized", () => { const response = { - "PAYMENT-RESPONSE": "c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8", + "PAYMENT-RESPONSE": "fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02", }; const txHash = extractX402TxHash(response); - expect(txHash).toBe("c1a7f0c3e8d9b5a2f7e4d1c8b9a6f3e0d7c4b1a8f5e2d9c6b3a0f7e4d1c8"); + expect(txHash).toBe("fc552f181bd318b300429b36c37e12e11abc8b1281fa726f75472b777c122e02"); }); it("payment-response lowercase header variant is recognized", () => { const response = { - "payment-response": "d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7", + "payment-response": "5d62c1eef81bc713ff1d984bef4cae6383594affc8d001b8737a278b136490c9", }; const txHash = extractX402TxHash(response); - expect(txHash).toBe("d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7"); + expect(txHash).toBe("5d62c1eef81bc713ff1d984bef4cae6383594affc8d001b8737a278b136490c9"); }); it("X-PAYMENT-RESPONSE header variant is recognized", () => { const response = { - "X-PAYMENT-RESPONSE": "e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", + "X-PAYMENT-RESPONSE": "9c3d6d3c9527ded9dcafc7e58e47d0a9ce85d71ecdcd139d1819aeb885e37f8b", }; const txHash = extractX402TxHash(response); - expect(txHash).toBe("e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8"); + expect(txHash).toBe("9c3d6d3c9527ded9dcafc7e58e47d0a9ce85d71ecdcd139d1819aeb885e37f8b"); }); it("refresh procedure: re-ping OZ facilitator settle endpoint", () => { From 2c340ef821655d81336be5f8276d00c7e1043eea Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 20:28:12 +0100 Subject: [PATCH 11/18] fix(ci): allowlist the generated test AGENT_SECRET_KEY in gitleaks config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checksum-valid testnet keypair added to tests/setup.ts (to replace the previous invalid-checksum fallback) matches .gitleaks.toml's stellar-secret-seed rule (S[A-Z2-7]{55}), since that pattern is inherent to the Stellar secret-key encoding format itself — any validly-formatted key, real or test fixture, will match it. Added a per-rule allowlist entry for this specific known-safe test value, verified locally against the exact commit range CI scans (gitleaks detect --log-opts="--no-merges --first-parent ..HEAD"), confirming zero leaks. --- .gitleaks.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitleaks.toml b/.gitleaks.toml index 1c37fe9..72ed7d9 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -8,6 +8,12 @@ description = "Stellar Secret Seed Key" regex = '''S[A-Z2-7]{55}''' tags = ["key", "stellar"] +[rules.allowlist] +description = "Checksum-valid testnet keypairs used only as test fixtures, never real secrets" +regexes = [ + '''SAQDKF5AKPWQZRXGHLH523VC7DU46U7IEQNOALOUO2A2UR55NWPVDRGF''', # tests/setup.ts fallback AGENT_SECRET_KEY +] + [[rules]] id = "groq-api-key" description = "Groq API Key" From 0737e9d00263a8d2aed727019f01a732179139b0 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:16:15 +0100 Subject: [PATCH 12/18] test(agent): fix DATA_DIR mismatch and stale fixtures in persistence/mock-network tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit persistence.test.ts hardcoded DATA_DIR from import.meta.url, ignoring that tests/setup.ts always sets process.env.DATA_DIR to a worker- specific temp dir — which is what tools.ts's own getDataDir() actually reads first. Every fsState.files lookup was checking the wrong key, so all writes appeared to silently vanish. Also updated the corrupt spending.json test to expect logger.error (not .warn — that's what the legacy-file recovery path actually calls) and to expect the current default tracker shape, which now includes monthTotals. mock-network.test.ts: AGENT_SECRET_KEY was "test-agent-secret", which doesn't start with "S" and tripped validateSignerKeyForNetwork()'s own prefix check before Keypair.fromSecret is ever reached (that part is mocked, but the prefix check isn't). Also added a beforeEach directory recreation, since tests/setup.ts's global afterEach wipes DATA_DIR after every test but tools.ts only creates the recipient dir once at module load, breaking the file's second test. --- agent/__tests__/mock-network.test.ts | 6 +++++- agent/__tests__/persistence.test.ts | 25 ++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/agent/__tests__/mock-network.test.ts b/agent/__tests__/mock-network.test.ts index 56fb16b..5e476b9 100644 --- a/agent/__tests__/mock-network.test.ts +++ b/agent/__tests__/mock-network.test.ts @@ -1,9 +1,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { mkdirSync } from "fs"; vi.hoisted(() => { process.env.MOCK_NETWORK = "1"; process.env.NODE_ENV = "test"; - process.env.AGENT_SECRET_KEY = "test-agent-secret"; + process.env.AGENT_SECRET_KEY = "S-test-agent-secret"; }); vi.mock("@stellar/stellar-sdk", () => ({ @@ -51,6 +52,9 @@ const POLICY = { describe("MOCK_NETWORK tool paths", () => { beforeEach(() => { + // tests/setup.ts wipes DATA_DIR after every test; recreate the recipient + // dir tools.ts otherwise only creates once at module load. + mkdirSync(`${process.env.DATA_DIR}/recipients/rosa`, { recursive: true }); resetSpendingTracker(); setSpendingPolicy(POLICY); }); diff --git a/agent/__tests__/persistence.test.ts b/agent/__tests__/persistence.test.ts index 33fdc61..d2a5155 100644 --- a/agent/__tests__/persistence.test.ts +++ b/agent/__tests__/persistence.test.ts @@ -113,16 +113,19 @@ vi.mock("../../shared/audit-log.ts", () => ({ vi.mock("../../shared/notifications.ts", () => ({ notify: vi.fn().mockResolvedValue(undefined) })); // Spying directly on the real pino instance is unreliable, so mock the // logger module the same way shared/__tests__/request-logger.test.ts does. -const warnSpy = vi.hoisted(() => vi.fn()); +const { warnSpy, errorSpy } = vi.hoisted(() => ({ warnSpy: vi.fn(), errorSpy: vi.fn() })); vi.mock("../../shared/logger.ts", () => ({ - logger: { info: vi.fn(), warn: warnSpy, error: vi.fn(), debug: vi.fn() }, + logger: { info: vi.fn(), warn: warnSpy, error: errorSpy, debug: vi.fn() }, })); import { describe, it, expect, beforeEach } from "vitest"; // Mirrors the DATA_DIR computation in agent/tools.ts so the fake fs paths // we seed/inspect line up regardless of where the repo is checked out. -const DATA_DIR = new URL("../../data", import.meta.url).pathname; +// tools.ts's getDataDir() checks process.env.DATA_DIR first — tests/setup.ts +// always sets it to a worker-specific temp dir, so that must take priority +// here too, or every fsState lookup misses the key tools.ts actually wrote. +const DATA_DIR = process.env.DATA_DIR || new URL("../../data", import.meta.url).pathname; function freshTracker(transactionCount: number) { return { @@ -146,6 +149,7 @@ beforeEach(() => { fsState.files.clear(); fsState.readOnlyPaths.clear(); warnSpy.mockClear(); + errorSpy.mockClear(); }); describe("Spending tracker persistence across restart (#44)", () => { @@ -202,8 +206,19 @@ describe("Corrupted data falls back to defaults without throwing (#44)", () => { result = tools.loadSpending("corrupt-recipient"); }).not.toThrow(); - expect(result).toEqual({ medications: 0, bills: 0, serviceFees: 0, transactions: [] }); - expect(warnSpy).toHaveBeenCalled(); + expect(result).toEqual({ + medications: 0, + bills: 0, + serviceFees: 0, + transactions: [], + monthTotals: { + yearMonth: expect.any(String), + medications: 0, + bills: 0, + serviceFees: 0, + }, + }); + expect(errorSpy).toHaveBeenCalled(); }); it("a corrupt policy.json on disk does not throw and yields the default policy", async () => { From de6a144fe2f510515f4c8fbecc1a5a1eebe451ab Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:25:31 +0100 Subject: [PATCH 13/18] fix(agent): wire up paybillSeqRetryTotal counter and fix its test fixtures getPaybillSeqRetryTotal()/resetPaybillSeqRetryTotal() existed but nothing ever incremented the underlying counter, so every assertion against it saw 0. Incremented it alongside the existing stellarTxBadSeqRetriesTotal metric at the one place tx_bad_seq retries actually happen (submitTransactionWithRetry's reload-and-rebuild loop). Also fixed paybill-seq-retry.test.ts's own bugs: its DEFAULT_POLICY had medicationMonthlyBudget + billMonthlyBudget (800) exceeding monthlyLimit (500), failing SpendingPolicySchema's cross-field refine before any payBill call could even run; its fs mock was missing appendFileSync/ renameSync (added by the #205 append-only log and #44 atomic-write work); and its "retry also fails" case queued only 2 rejections while the real retry loop always runs its full 3 reload/retry attempts, so it asserted a stale expected count. --- agent/__tests__/paybill-seq-retry.test.ts | 15 +++++++++------ agent/tools.ts | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/agent/__tests__/paybill-seq-retry.test.ts b/agent/__tests__/paybill-seq-retry.test.ts index d07117c..112be0c 100644 --- a/agent/__tests__/paybill-seq-retry.test.ts +++ b/agent/__tests__/paybill-seq-retry.test.ts @@ -17,8 +17,10 @@ vi.mock("dotenv/config", () => ({})); vi.mock("fs", () => ({ readFileSync: vi.fn().mockReturnValue("{}"), writeFileSync: vi.fn(), + appendFileSync: vi.fn(), existsSync: vi.fn().mockReturnValue(false), mkdirSync: vi.fn(), + renameSync: vi.fn(), })); vi.mock("@stellar/stellar-sdk", () => ({ Keypair: { @@ -70,8 +72,8 @@ import { const DEFAULT_POLICY = { dailyLimit: 100, monthlyLimit: 500, - medicationMonthlyBudget: 300, - billMonthlyBudget: 500, + medicationMonthlyBudget: 200, + billMonthlyBudget: 300, approvalThreshold: 75, }; @@ -120,15 +122,16 @@ describe("payBill — tx_bad_seq retry (Issues #197 / #282)", () => { it("surfaces error when retry also fails", async () => { mockLoadAccount.mockResolvedValue(mockAccount); - mockSubmitTransaction - .mockRejectedValueOnce(makeSeqError()) - .mockRejectedValueOnce(makeSeqError()); + // Every attempt (initial + all inner reload/retry cycles) keeps failing + // with a sequence error, so the retry loop exhausts its bounded attempts + // and surfaces the final error instead of retrying forever. + mockSubmitTransaction.mockRejectedValue(makeSeqError()); const result = await payBill("provider-1", "General Hospital", "ER Visit", 50); expect(result.success).toBe(false); expect((result as any).error).toContain("Stellar USDC transfer failed"); - expect(getPaybillSeqRetryTotal()).toBe(1); + expect(getPaybillSeqRetryTotal()).toBe(3); }); it("does not retry on non-sequence errors", async () => { diff --git a/agent/tools.ts b/agent/tools.ts index d23051f..b48e8ef 100644 --- a/agent/tools.ts +++ b/agent/tools.ts @@ -286,6 +286,7 @@ export async function submitTransactionWithRetry( break; } stellarTxBadSeqRetriesTotal.inc(); + paybillSeqRetryTotal++; logger.warn( { seq: tx?.sequence, attempt: seqRetry + 1, reason: "tx_bad_seq" }, "[Stellar] tx_bad_seq — reloaded account, retrying", From 58427393efe87c0500e1af932858066aecc8f5fe Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:33:21 +0100 Subject: [PATCH 14/18] fix(agent): restore lost signer-hint verification in the fee-bump path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildAndSubmitUsdcPayment contained a signer-hint check (verifying the signed envelope's signature hint matches the expected signer's before ever calling submitTransaction) but was dead code — payBill actually calls submitTransactionWithFeeBump, a different code path introduced later for fee-bump/tx_bad_seq retry support, which never got this safety check. Moved the check into submitTransactionWithFeeBump's buildInner() (shared by payBill and payForMedication) and deleted the now-fully-unused buildAndSubmitUsdcPayment. This is a real safety check for a live-money code path — it refuses to submit a Stellar transaction whose signature doesn't match the expected signer — so agent/__tests__/stellar-signature.test.ts's 3 assertions on it now actually exercise real behavior instead of dead code. Since submitTransactionWithFeeBump is also unit-tested directly with hand-rolled signer stubs, added the previously-unnecessary signatureHint() mock to those stubs in fee-bump-doubling.test.ts and fee-bump-retry-budget.test.ts — real Keypair objects always have this method, only these ad-hoc test doubles didn't. --- agent/__tests__/fee-bump-doubling.test.ts | 6 ++++- agent/__tests__/fee-bump-retry-budget.test.ts | 10 ++++---- agent/tools.ts | 24 ++++--------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/agent/__tests__/fee-bump-doubling.test.ts b/agent/__tests__/fee-bump-doubling.test.ts index cdc8616..6d1a1f7 100644 --- a/agent/__tests__/fee-bump-doubling.test.ts +++ b/agent/__tests__/fee-bump-doubling.test.ts @@ -90,7 +90,11 @@ import { const MAX_FEE_STROOPS = 100_000; // matches the MAX_FEE_STROOPS default in tools.ts const mockAccount = { id: "GPUB123", sequence: "1" }; const mockServer = { loadAccount: mockLoadAccount, submitTransaction: mockSubmitTransaction } as any; -const signer = { publicKey: () => "GPUB123", sign: () => {} } as any; +const signer = { + publicKey: () => "GPUB123", + sign: () => {}, + signatureHint: () => MOCK_HINT, +} as any; function horizonError(code: string) { const err: any = new Error("Request failed with status code 400"); diff --git a/agent/__tests__/fee-bump-retry-budget.test.ts b/agent/__tests__/fee-bump-retry-budget.test.ts index b18b32b..a6109bb 100644 --- a/agent/__tests__/fee-bump-retry-budget.test.ts +++ b/agent/__tests__/fee-bump-retry-budget.test.ts @@ -118,7 +118,7 @@ describe("submitTransactionWithFeeBump — 35s retry budget (Issue #797)", () => mockServer, mockAccount, [{}], - { publicKey: () => "GPUB123", sign: () => {} } as any, + { publicKey: () => "GPUB123", sign: () => {}, signatureHint: () => MOCK_HINT } as any, "100", clock, ), @@ -145,7 +145,7 @@ describe("submitTransactionWithFeeBump — 35s retry budget (Issue #797)", () => mockServer, mockAccount, [{}], - { publicKey: () => "GPUB123", sign: () => {} } as any, + { publicKey: () => "GPUB123", sign: () => {}, signatureHint: () => MOCK_HINT } as any, "100", clock, ), @@ -174,7 +174,7 @@ describe("submitTransactionWithFeeBump — 35s retry budget (Issue #797)", () => mockServer, mockAccount, [{}], - { publicKey: () => "GPUB123", sign: () => {} } as any, + { publicKey: () => "GPUB123", sign: () => {}, signatureHint: () => MOCK_HINT } as any, "100", clock, ), @@ -196,7 +196,7 @@ describe("submitTransactionWithFeeBump — 35s retry budget (Issue #797)", () => mockServer, mockAccount, [{}], - { publicKey: () => "GPUB123", sign: () => {} } as any, + { publicKey: () => "GPUB123", sign: () => {}, signatureHint: () => MOCK_HINT } as any, "100", clock, ); @@ -216,7 +216,7 @@ describe("submitTransactionWithFeeBump — 35s retry budget (Issue #797)", () => mockServer, mockAccount, [{}], - { publicKey: () => "GPUB123", sign: () => {} } as any, + { publicKey: () => "GPUB123", sign: () => {}, signatureHint: () => MOCK_HINT } as any, "100", clock, ).catch(() => {}); diff --git a/agent/tools.ts b/agent/tools.ts index b48e8ef..afba235 100644 --- a/agent/tools.ts +++ b/agent/tools.ts @@ -363,6 +363,10 @@ export async function submitTransactionWithFeeBump( } const built = builder.setTimeout(30).build(); built.sign(signer); + const sigHint = built.signatures[0]?.hint(); + if (!sigHint || !sigHint.equals(signer.signatureHint())) { + throw new Error(`Signer mismatch: expected ${signer.publicKey()} — refusing to submit`); + } return built; }; @@ -2173,26 +2177,6 @@ export async function payForMedication( return { success: true, transaction: tx }; } -// Helper: build, sign, and submit a single USDC payment so payBill can retry on tx_bad_seq -async function buildAndSubmitUsdcPayment(account: any, recipientKey: string, amount: number): Promise { - const usdcAsset = new Asset("USDC", USDC_ISSUER); - const stellarTx = new TransactionBuilder(account, { - fee: "100", - networkPassphrase: Networks.TESTNET, - }) - .addOperation(Operation.payment({ destination: recipientKey, asset: usdcAsset, amount: amount.toFixed(7) })) - .setTimeout(30) - .build(); - stellarTx.sign(agentKeypair); - const sigHint = stellarTx.signatures[0]?.hint(); - if (!sigHint || !sigHint.equals(agentKeypair.signatureHint())) { - throw new Error(`Signer mismatch: expected ${agentKeypair.publicKey()} — refusing to submit`); - } - console.log(` [Stellar] Signer verified: ${agentKeypair.publicKey().slice(0, 8)}...`); - const result = await horizonServer.submitTransaction(stellarTx); - return (result as any).hash; -} - // --- Tool: Pay a medical bill via real Stellar USDC transfer --- export async function payBill( providerId: string, From 2c330815dee3f7a96de41c293ee5aa22abf6c30a Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:38:23 +0100 Subject: [PATCH 15/18] test(agent): fix policy invariant violations and missing MOCK_NETWORK=0 in pay-for-medication.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two "daily/monthly cap" test cases overrode individual SpendingPolicy fields without keeping the schema's cross-field invariants satisfied (approvalThreshold <= dailyLimit, dailyLimit <= monthlyLimit), so setSpendingPolicy() threw before the test logic under test ever ran. The MPP-failure and success-path tests never set MOCK_NETWORK=0, so payForMedication always took the built-in mock-network branch (which unconditionally fakes success) instead of exercising the real createMppClient() -> Mppx.create() path the test's mockMppFetch spy is wired to intercept — every assertion checked the hardcoded mock response instead of the configured mockMppFetch behavior. --- agent/__tests__/pay-for-medication.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/agent/__tests__/pay-for-medication.test.ts b/agent/__tests__/pay-for-medication.test.ts index f818781..3d43f2a 100644 --- a/agent/__tests__/pay-for-medication.test.ts +++ b/agent/__tests__/pay-for-medication.test.ts @@ -7,6 +7,9 @@ const { mockMppFetch, onProgressHolder, mockFiles, MOCK_HINT } = vi.hoisted(() => { process.env.AGENT_SECRET_KEY = "SBWWZYCAFDDJXNRRMKSFNRB6OTVZHTCMPUCVZ4FBZLSPHFKHYLPRTJCD"; process.env.BILL_PROVIDER_PUBLIC_KEY = "GBILLPROVIDER"; + // Disable mock network so getMppClient() goes through the real + // createMppClient() -> Mppx.create() path that mockMppFetch observes. + process.env.MOCK_NETWORK = "0"; const onProgressHolder: { fn?: (event: any) => void } = {}; return { mockMppFetch: vi.fn(), onProgressHolder, mockFiles: new Map(), MOCK_HINT: Buffer.from([0xca, 0xfe, 0xba, 0xbe]) }; }); @@ -132,7 +135,7 @@ describe("payForMedication — policy-blocked (Issue #35)", () => { }); it("returns success:false when daily limit would be exceeded", async () => { - setSpendingPolicy("rosa", { ...DEFAULT_POLICY, dailyLimit: 10 }); + setSpendingPolicy("rosa", { ...DEFAULT_POLICY, dailyLimit: 10, approvalThreshold: 5 }); const r = await payForMedication("p1", "Pharma", "Drug", 50); expect(r.success).toBe(false); expect(r.error).toContain("BLOCKED BY SPENDING POLICY"); @@ -315,11 +318,11 @@ describe("checkSpendingPolicy — basic rules (Issue #35)", () => { it("blocks medication + bill spending at the global monthly cap", async () => { setSpendingPolicy("rosa", { ...DEFAULT_POLICY, - dailyLimit: 500, + dailyLimit: 120, monthlyLimit: 120, medicationMonthlyBudget: 80, billMonthlyBudget: 40, - approvalThreshold: 500, + approvalThreshold: 120, }); mockMppFetch.mockResolvedValueOnce({ json: async () => ({ success: true, order: { id: "order-global-cap" } }), From 623e2a69a01125da5774734de652e3bb65872703 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:41:03 +0100 Subject: [PATCH 16/18] fix(agent): wire STELLAR_TIMEBOUNDS_SECONDS into the fee-bump envelope timeout STELLAR_TIMEBOUNDS_SECONDS (env-configurable, defaults to 60) was parsed at module load but buildInner() hardcoded .setTimeout(30) instead of using it, so the env var had no effect on the actual transaction timebounds. --- agent/tools.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/tools.ts b/agent/tools.ts index afba235..f6620a0 100644 --- a/agent/tools.ts +++ b/agent/tools.ts @@ -361,7 +361,7 @@ export async function submitTransactionWithFeeBump( for (const op of operations) { builder.addOperation(op); } - const built = builder.setTimeout(30).build(); + const built = builder.setTimeout(STELLAR_TIMEBOUNDS_SECONDS).build(); built.sign(signer); const sigHint = built.signatures[0]?.hint(); if (!sigHint || !sigHint.equals(signer.signatureHint())) { From d403d13dd47a5d95b510aed57ff4aee922ff10f6 Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Thu, 30 Jul 2026 21:50:06 +0100 Subject: [PATCH 17/18] test(agent): fix fs mock and dead assignment in timezone-policy.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs combined to make the per-policy-timezone daily-limit check untestable: 1. The fs mock's existsSync/readFileSync only recognized spending*.json paths by pattern-matching the filename; for policy.json it always returned existsSync=false, so loadPolicy() always fell back to the module's internal default (timezone-less) policy no matter what setSpendingPolicy() "saved" — the Phoenix timezone this test sets was silently dropped, so checkSpendingPolicy() always used the global SPENDING_TIMEZONE=UTC day boundary instead. Replaced the ad-hoc pattern-matched mock with a real in-memory file map (same approach as agent/__tests__/pay-for-medication.test.ts) that correctly persists whatever tools.ts actually writes. 2. Both tests injected a transaction via `tools.loadSpending("rosa").transactions = [...]` — loadSpending() returns a fresh, disconnected read from disk, not the live module-internal tracker checkSpendingPolicy() reads from. Switched to getSpendingTracker().transactions.push(...), whose shallow copy preserves the same array reference as the real tracker. --- agent/__tests__/timezone-policy.test.ts | 91 +++++++++++++------------ 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/agent/__tests__/timezone-policy.test.ts b/agent/__tests__/timezone-policy.test.ts index 545d1c9..4c85962 100644 --- a/agent/__tests__/timezone-policy.test.ts +++ b/agent/__tests__/timezone-policy.test.ts @@ -10,34 +10,38 @@ * UTC day. */ -const { MOCK_HINT } = vi.hoisted(() => { +const { MOCK_HINT, mockFiles } = vi.hoisted(() => { process.env.AGENT_SECRET_KEY = "SBWWZYCAFDDJXNRRMKSFNRB6OTVZHTCMPUCVZ4FBZLSPHFKHYLPRTJCD"; process.env.MOCK_NETWORK = "1"; // Global env is UTC to clearly distinguish it from the per-policy Phoenix tz process.env.SPENDING_TIMEZONE = "UTC"; - return { MOCK_HINT: Buffer.from([0xca, 0xfe, 0xba, 0xbe]) }; + return { MOCK_HINT: Buffer.from([0xca, 0xfe, 0xba, 0xbe]), mockFiles: new Map() }; }); vi.mock("dotenv/config", () => ({})); +// A real in-memory fs, not just spending-file-shaped stubs: loadPolicy() reads +// policy.json via existsSync/readFileSync too, and setSpendingPolicy() writes +// it via writeFileSync — a mock that only understands spending*.json makes +// existsSync(policyFile) always false, so the Phoenix timezone this test sets +// via setSpendingPolicy() is silently dropped and checkSpendingPolicy() always +// falls back to the default (timezone-less) policy. vi.mock("fs", () => ({ - readFileSync: vi.fn((filePath: string) => { - const key = String(filePath); - if (key.includes("spending.snapshot.json")) { - return JSON.stringify({ medications: 0, bills: 0, serviceFees: 0, transactions: [], _snapshotTxCount: 0 }); - } - if (key.includes("spending.json")) { - return JSON.stringify({ medications: 0, bills: 0, serviceFees: 0, transactions: [] }); - } - return "{}"; + readFileSync: vi.fn((filePath: string) => mockFiles.get(String(filePath)) ?? "{}"), + writeFileSync: vi.fn((filePath: string, data: string) => { + mockFiles.set(String(filePath), String(data)); }), - writeFileSync: vi.fn(), - appendFileSync: vi.fn(), - existsSync: vi.fn((filePath: string) => { - const key = String(filePath); - return key.includes("spending.snapshot.json") || key.includes("spending.json"); + appendFileSync: vi.fn((filePath: string, data: string) => { + mockFiles.set(String(filePath), (mockFiles.get(String(filePath)) ?? "") + String(data)); }), + existsSync: vi.fn((filePath: string) => mockFiles.has(String(filePath))), mkdirSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn((from: string, to: string) => { + const data = mockFiles.get(String(from)); + if (data !== undefined) { + mockFiles.set(String(to), data); + mockFiles.delete(String(from)); + } + }), })); vi.mock("@stellar/stellar-sdk", () => ({ Keypair: { @@ -108,20 +112,21 @@ describe("Per-policy timezone daily-limit check (Issue #207)", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2024-01-14T22:00:00.000Z")); // 3 pm Phoenix Jan 14 - const tracker = tools.loadSpending("rosa"); - // Inject the 11pm Phoenix transaction directly into the in-memory tracker - tracker.transactions = [ - { - id: "tx-phoenix-11pm", - timestamp: phoenixElevenPm, - type: "medication" as const, - description: "Late-night medication", - amount: 40, - recipient: "pharm-1", - status: "completed" as const, - category: TRANSACTION_CATEGORY.MEDICATIONS, - }, - ] as any; + // getSpendingTracker()'s shallow copy keeps the same .transactions array + // reference as the live module-internal tracker checkSpendingPolicy reads + // from — loadSpending() reads a disconnected fresh-from-disk copy, so + // mutating it has no effect on the state under test. + const tracker = tools.getSpendingTracker(); + tracker.transactions.push({ + id: "tx-phoenix-11pm", + timestamp: phoenixElevenPm, + type: "medication" as const, + description: "Late-night medication", + amount: 40, + recipient: "pharm-1", + status: "completed" as const, + category: TRANSACTION_CATEGORY.MEDICATIONS, + } as any); // Set a policy with Phoenix timezone, daily limit $100, meds budget $300 tools.setSpendingPolicy({ @@ -153,19 +158,17 @@ describe("Per-policy timezone daily-limit check (Issue #207)", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2024-01-14T22:00:00.000Z")); - const tracker = tools.loadSpending("rosa"); - tracker.transactions = [ - { - id: "tx-phoenix-11pm-utc", - timestamp: phoenixElevenPm, - type: "medication" as const, - description: "Late-night medication", - amount: 40, - recipient: "pharm-1", - status: "completed" as const, - category: TRANSACTION_CATEGORY.MEDICATIONS, - }, - ] as any; + const tracker = tools.getSpendingTracker(); + tracker.transactions.push({ + id: "tx-phoenix-11pm-utc", + timestamp: phoenixElevenPm, + type: "medication" as const, + description: "Late-night medication", + amount: 40, + recipient: "pharm-1", + status: "completed" as const, + category: TRANSACTION_CATEGORY.MEDICATIONS, + } as any); // Policy WITHOUT timezone → falls back to SPENDING_TIMEZONE=UTC tools.setSpendingPolicy({ From dcecf2f73ebdeed1dac8ae76ee2e4946b94654fc Mon Sep 17 00:00:00 2001 From: JONAH-6 Date: Mon, 3 Aug 2026 13:58:05 +0100 Subject: [PATCH 18/18] chore: disable Claude Code commit co-author attribution for this repo Sets attribution.commit to empty in .claude/settings.json so future commits made with Claude Code in this repository don't append a Co-Authored-By trailer. --- .claude/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..ce5d273 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "attribution": { + "commit": "" + } +}