From d43b9f5298cbb189138f49ebd7705574f6ee77b4 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Tue, 14 Apr 2026 10:10:22 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20TokenVault=20MVP=20=E2=80=94=20hybrid?= =?UTF-8?q?=20MCP=20+=20proxy=20for=20LLM=20token=20savings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4-package monorepo: - @tokenvault/core: Fastify 5.x proxy server, 7-stage pipeline, 3 provider adapters (Anthropic, OpenAI, Ollama), PostgreSQL ticket system, cost calculator with real provider pricing - @tokenvault/mcp: MCP server (stdio) with tv_ticket, tv_cost, tv_health tools for IDE integration - @tokenvault/client: TypeScript SDK with createTokenVaultClient() - @tokenvault/dashboard: Single-file HTML dashboard with MAGATAMA CI style (indigo #6366f1), bilingual DE+EN, 4 tabs OpenAI-compatible proxy at /v1/chat/completions — drop-in replacement. Every LLM request becomes a trackable ticket (TV-00001). --- .gitignore | 11 + .pnpmconfig.json | 1 + LICENSE | 19 + deploy.sh | 41 + ecosystem.config.cjs | 50 + package.json | 19 + packages/client/package.json | 31 + packages/client/src/index.ts | 119 + packages/client/tsconfig.json | 8 + packages/client/tsup.config.ts | 11 + packages/core/package.json | 32 + packages/core/src/config.ts | 28 + packages/core/src/db/client.ts | 37 + packages/core/src/db/migrate.ts | 125 + packages/core/src/observability/logger.ts | 10 + packages/core/src/pipeline/index.ts | 119 + packages/core/src/providers/index.ts | 275 ++ packages/core/src/routes/health.ts | 19 + packages/core/src/routes/proxy.ts | 69 + packages/core/src/routes/tickets.ts | 57 + packages/core/src/server.ts | 53 + packages/core/src/tickets/ticket-service.ts | 157 ++ packages/core/src/types.ts | 151 + packages/core/tsconfig.json | 8 + packages/core/tsup.config.ts | 14 + packages/dashboard/package.json | 25 + packages/dashboard/public/index.html | 286 ++ packages/dashboard/src/server.ts | 51 + packages/dashboard/tsconfig.json | 8 + packages/dashboard/tsup.config.ts | 14 + packages/mcp/package.json | 28 + packages/mcp/src/server.ts | 85 + packages/mcp/tsconfig.json | 8 + packages/mcp/tsup.config.ts | 15 + pnpm-lock.yaml | 2730 +++++++++++++++++++ pnpm-workspace.yaml | 2 + tsconfig.json | 17 + 37 files changed, 4733 insertions(+) create mode 100644 .gitignore create mode 100644 .pnpmconfig.json create mode 100644 LICENSE create mode 100755 deploy.sh create mode 100644 ecosystem.config.cjs create mode 100644 package.json create mode 100644 packages/client/package.json create mode 100644 packages/client/src/index.ts create mode 100644 packages/client/tsconfig.json create mode 100644 packages/client/tsup.config.ts create mode 100644 packages/core/package.json create mode 100644 packages/core/src/config.ts create mode 100644 packages/core/src/db/client.ts create mode 100644 packages/core/src/db/migrate.ts create mode 100644 packages/core/src/observability/logger.ts create mode 100644 packages/core/src/pipeline/index.ts create mode 100644 packages/core/src/providers/index.ts create mode 100644 packages/core/src/routes/health.ts create mode 100644 packages/core/src/routes/proxy.ts create mode 100644 packages/core/src/routes/tickets.ts create mode 100644 packages/core/src/server.ts create mode 100644 packages/core/src/tickets/ticket-service.ts create mode 100644 packages/core/src/types.ts create mode 100644 packages/core/tsconfig.json create mode 100644 packages/core/tsup.config.ts create mode 100644 packages/dashboard/package.json create mode 100644 packages/dashboard/public/index.html create mode 100644 packages/dashboard/src/server.ts create mode 100644 packages/dashboard/tsconfig.json create mode 100644 packages/dashboard/tsup.config.ts create mode 100644 packages/mcp/package.json create mode 100644 packages/mcp/src/server.ts create mode 100644 packages/mcp/tsconfig.json create mode 100644 packages/mcp/tsup.config.ts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1f7ccf --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules/ +dist/ +.env +.env.* +.dev.vars +wrangler.toml +*.local +*.log +.DS_Store +coverage/ +.turbo/ diff --git a/.pnpmconfig.json b/.pnpmconfig.json new file mode 100644 index 0000000..0e4b1ed --- /dev/null +++ b/.pnpmconfig.json @@ -0,0 +1 @@ +{"onlyBuiltDependenciesFile": "","ignoredBuiltDependencies": [],"onlyBuiltDependencies": ["esbuild"]} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..53a9200 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + Copyright 2026 Context X (context-x.org) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..57b400c --- /dev/null +++ b/deploy.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -euo pipefail + +REMOTE="root@82.165.222.127" +REMOTE_DIR="/opt/tokenvault" +LOCAL_DIR="$(cd "$(dirname "$0")" && pwd)" + +echo "=== TokenVault Deploy ===" + +# 1. Build locally +echo "[1/5] Building..." +cd "$LOCAL_DIR" +pnpm build + +# 2. Sync to Erik +echo "[2/5] Syncing to Erik..." +rsync -avz --delete \ + --exclude='node_modules' \ + --exclude='.env' \ + --exclude='.git' \ + --exclude='coverage' \ + "$LOCAL_DIR/" "$REMOTE:$REMOTE_DIR/" + +# 3. Install dependencies on Erik +echo "[3/5] Installing dependencies..." +ssh "$REMOTE" "cd $REMOTE_DIR && pnpm install --prod --frozen-lockfile 2>/dev/null || npm install --omit=dev" + +# 4. Create DB if needed +echo "[4/5] Setting up database..." +ssh "$REMOTE" "sudo -u postgres psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='tokenvault'\" | grep -q 1 || sudo -u postgres createuser tokenvault" +ssh "$REMOTE" "sudo -u postgres psql -tc \"SELECT 1 FROM pg_database WHERE datname='tokenvault'\" | grep -q 1 || sudo -u postgres createdb -O tokenvault tokenvault" +ssh "$REMOTE" "mkdir -p $REMOTE_DIR/logs" + +# 5. Start/restart PM2 +echo "[5/5] Starting PM2 processes..." +ssh "$REMOTE" "cd $REMOTE_DIR && pm2 startOrRestart ecosystem.config.cjs && pm2 save" + +echo "" +echo "=== Deploy complete ===" +echo "Core: http://localhost:3300/health" +echo "Dashboard: http://localhost:3301" diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs new file mode 100644 index 0000000..46813f0 --- /dev/null +++ b/ecosystem.config.cjs @@ -0,0 +1,50 @@ +module.exports = { + apps: [ + { + name: 'tokenvault', + script: 'packages/core/dist/server.js', + cwd: '/opt/tokenvault', + env: { + NODE_ENV: 'production', + TOKENVAULT_PORT: '3300', + TOKENVAULT_HOST: '0.0.0.0', + DB_HOST: '127.0.0.1', + DB_PORT: '5432', + DB_NAME: 'tokenvault', + DB_USER: 'tokenvault', + QDRANT_URL: 'http://localhost:6333', + OLLAMA_URL: 'https://ollama.fichtmueller.org', + }, + instances: 1, + exec_mode: 'fork', + max_memory_restart: '512M', + error_file: '/opt/tokenvault/logs/error.log', + out_file: '/opt/tokenvault/logs/out.log', + merge_logs: true, + time: true, + watch: false, + autorestart: true, + max_restarts: 10, + restart_delay: 5000, + }, + { + name: 'tokenvault-dashboard', + script: 'packages/dashboard/dist/server.js', + cwd: '/opt/tokenvault', + env: { + NODE_ENV: 'production', + PORT: '3301', + TOKENVAULT_CORE_URL: 'http://localhost:3300', + }, + instances: 1, + exec_mode: 'fork', + max_memory_restart: '256M', + error_file: '/opt/tokenvault/logs/dashboard-error.log', + out_file: '/opt/tokenvault/logs/dashboard-out.log', + merge_logs: true, + time: true, + watch: false, + autorestart: true, + }, + ], +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..9b1c0ac --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "tokenvault", + "version": "0.1.0", + "private": true, + "description": "Hybrid MCP + Proxy platform for LLM token savings, multi-provider routing, and ticket-based cost tracking", + "license": "Apache-2.0", + "author": "Context X ", + "scripts": { + "build": "pnpm -r build", + "dev": "pnpm -r --parallel dev", + "test": "pnpm -r test", + "lint": "pnpm -r lint", + "clean": "pnpm -r clean" + }, + "engines": { + "node": ">=20.0.0", + "pnpm": ">=9.0.0" + } +} diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 0000000..ab49da3 --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,31 @@ +{ + "name": "@tokenvault/client", + "version": "0.1.0", + "description": "TokenVault TypeScript SDK — wrap LLM provider SDKs with auto-tracking", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "Apache-2.0", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsx watch src/index.ts", + "test": "vitest run", + "clean": "rm -rf dist" + }, + "dependencies": { + "zod": "^3.24.0" + }, + "devDependencies": { + "tsup": "^8.4.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^3.1.0", + "@types/node": "^22.0.0" + } +} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts new file mode 100644 index 0000000..8916030 --- /dev/null +++ b/packages/client/src/index.ts @@ -0,0 +1,119 @@ +export interface TokenVaultConfig { + readonly baseUrl: string; + readonly caller?: string; + readonly project?: string; + readonly team?: string; + readonly timeout?: number; +} + +export interface CompletionRequest { + readonly model: string; + readonly messages: Array<{ role: string; content: string }>; + readonly temperature?: number; + readonly max_tokens?: number; + readonly caller?: string; + readonly project?: string; + readonly team?: string; +} + +export interface CompletionResponse { + readonly id: string; + readonly model: string; + readonly choices: Array<{ + readonly index: number; + readonly message: { readonly role: string; readonly content: string }; + readonly finish_reason: string; + }>; + readonly usage: { + readonly prompt_tokens: number; + readonly completion_tokens: number; + readonly total_tokens: number; + readonly cached_tokens?: number; + }; + readonly tokenvault?: { + readonly ticket_id: string; + readonly ticket_number: string; + readonly provider: string; + readonly cost_usd: number; + readonly tokens_saved: number; + readonly latency_ms: number; + }; +} + +export interface CostSummary { + readonly period: string; + readonly total_cost_usd: number; + readonly total_saved_usd: number; + readonly total_tokens_in: number; + readonly total_tokens_out: number; + readonly total_requests: number; + readonly cache_hit_rate: number; +} + +export class TokenVaultClient { + private readonly baseUrl: string; + private readonly defaults: { caller?: string; project?: string; team?: string }; + private readonly timeout: number; + + constructor(config: TokenVaultConfig) { + this.baseUrl = config.baseUrl.replace(/\/$/, ''); + this.defaults = { caller: config.caller, project: config.project, team: config.team }; + this.timeout = config.timeout ?? 30_000; + } + + async completion(request: CompletionRequest): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), this.timeout); + + try { + const res = await fetch(`${this.baseUrl}/v1/chat/completions`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + ...request, + caller: request.caller ?? this.defaults.caller, + project: request.project ?? this.defaults.project, + team: request.team ?? this.defaults.team, + }), + signal: controller.signal, + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`TokenVault error ${res.status}: ${errText.slice(0, 200)}`); + } + + return res.json() as Promise; + } finally { + clearTimeout(timer); + } + } + + async safeCompletion(request: CompletionRequest): Promise { + try { + return await this.completion(request); + } catch { + return null; + } + } + + async health(): Promise> { + const res = await fetch(`${this.baseUrl}/health`); + return res.json() as Promise>; + } + + async cost(period: string = 'month'): Promise { + const res = await fetch(`${this.baseUrl}/v1/cost?period=${period}`); + return res.json() as Promise; + } + + async tickets(params?: Record): Promise { + const qs = params ? `?${new URLSearchParams(params)}` : ''; + const res = await fetch(`${this.baseUrl}/v1/tickets${qs}`); + return res.json(); + } +} + +export function createTokenVaultClient(config: TokenVaultConfig): TokenVaultClient { + return new TokenVaultClient(config); +} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json new file mode 100644 index 0000000..a086b14 --- /dev/null +++ b/packages/client/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/client/tsup.config.ts b/packages/client/tsup.config.ts new file mode 100644 index 0000000..074b7c6 --- /dev/null +++ b/packages/client/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm'], + target: 'node20', + outDir: 'dist', + clean: true, + sourcemap: true, + dts: true, +}); diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..926b05a --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,32 @@ +{ + "name": "@tokenvault/core", + "version": "0.1.0", + "description": "TokenVault central proxy server — multi-provider routing, ticket tracking, semantic caching", + "type": "module", + "main": "dist/server.js", + "types": "dist/server.d.ts", + "license": "Apache-2.0", + "scripts": { + "build": "tsup", + "dev": "tsx watch src/server.ts", + "test": "vitest run", + "test:watch": "vitest", + "clean": "rm -rf dist" + }, + "dependencies": { + "fastify": "^5.3.0", + "@fastify/cors": "^11.0.0", + "pg": "^8.13.0", + "pino": "^9.6.0", + "zod": "^3.24.0", + "nanoid": "^5.1.0" + }, + "devDependencies": { + "tsup": "^8.4.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^3.1.0", + "@types/node": "^22.0.0", + "@types/pg": "^8.11.0" + } +} diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts new file mode 100644 index 0000000..f70a93a --- /dev/null +++ b/packages/core/src/config.ts @@ -0,0 +1,28 @@ +export const config = { + port: parseInt(process.env['TOKENVAULT_PORT'] ?? '3300', 10), + host: process.env['TOKENVAULT_HOST'] ?? '0.0.0.0', + nodeEnv: process.env['NODE_ENV'] ?? 'development', + + db: { + host: process.env['DB_HOST'] ?? '127.0.0.1', + port: parseInt(process.env['DB_PORT'] ?? '5432', 10), + name: process.env['DB_NAME'] ?? 'tokenvault', + user: process.env['DB_USER'] ?? 'tokenvault', + password: process.env['DB_PASSWORD'] ?? '', + }, + + qdrant: { + url: process.env['QDRANT_URL'] ?? 'http://localhost:6333', + collection: process.env['QDRANT_COLLECTION'] ?? 'tokenvault_cache', + }, + + providers: { + anthropic: { apiKey: process.env['ANTHROPIC_API_KEY'] ?? '' }, + openai: { apiKey: process.env['OPENAI_API_KEY'] ?? '' }, + google: { apiKey: process.env['GOOGLE_AI_API_KEY'] ?? '' }, + mistral: { apiKey: process.env['MISTRAL_API_KEY'] ?? '' }, + groq: { apiKey: process.env['GROQ_API_KEY'] ?? '' }, + cerebras: { apiKey: process.env['CEREBRAS_API_KEY'] ?? '' }, + ollama: { url: process.env['OLLAMA_URL'] ?? 'http://localhost:11434' }, + }, +} as const; diff --git a/packages/core/src/db/client.ts b/packages/core/src/db/client.ts new file mode 100644 index 0000000..13be280 --- /dev/null +++ b/packages/core/src/db/client.ts @@ -0,0 +1,37 @@ +import pg from 'pg'; +import { config } from '../config.js'; +import { logger } from '../observability/logger.js'; + +const { Pool } = pg; + +const pool = new Pool({ + host: config.db.host, + port: config.db.port, + database: config.db.name, + user: config.db.user, + password: config.db.password, + max: 20, + idleTimeoutMillis: 30_000, + connectionTimeoutMillis: 5_000, +}); + +pool.on('error', (err) => { + logger.error({ err }, 'Unexpected PostgreSQL pool error'); +}); + +export async function query( + text: string, + params?: readonly unknown[], +): Promise> { + const start = Date.now(); + const result = await pool.query(text, params as unknown[]); + const latency = Date.now() - start; + if (latency > 500) { + logger.warn({ latency, query: text.slice(0, 80) }, 'Slow query detected'); + } + return result; +} + +export async function closePool(): Promise { + await pool.end(); +} diff --git a/packages/core/src/db/migrate.ts b/packages/core/src/db/migrate.ts new file mode 100644 index 0000000..341d458 --- /dev/null +++ b/packages/core/src/db/migrate.ts @@ -0,0 +1,125 @@ +import { query } from './client.js'; +import { logger } from '../observability/logger.js'; + +const MIGRATIONS = [ + // ─── tickets (core table, TimescaleDB hypertable) ───────────────────── + `CREATE TABLE IF NOT EXISTS tickets ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + ticket_number BIGSERIAL, + session_id UUID, + provider TEXT NOT NULL, + model TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'completed', + tokens_in INTEGER NOT NULL DEFAULT 0, + tokens_out INTEGER NOT NULL DEFAULT 0, + tokens_cached INTEGER NOT NULL DEFAULT 0, + tokens_saved INTEGER NOT NULL DEFAULT 0, + cost_usd NUMERIC(12,8) NOT NULL DEFAULT 0, + cost_saved_usd NUMERIC(12,8) NOT NULL DEFAULT 0, + latency_ms INTEGER NOT NULL DEFAULT 0, + cache_hit BOOLEAN NOT NULL DEFAULT false, + compression_mode TEXT, + compression_ratio NUMERIC(5,2), + caller TEXT, + project TEXT, + team TEXT, + input_hash TEXT, + output_hash TEXT, + metadata JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() + )`, + + // ─── sessions ───────────────────────────────────────────────────────── + `CREATE TABLE IF NOT EXISTS sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + caller TEXT NOT NULL, + project TEXT, + team TEXT, + started_at TIMESTAMPTZ DEFAULT NOW(), + ended_at TIMESTAMPTZ, + ticket_count INTEGER DEFAULT 0, + total_cost NUMERIC(12,8) DEFAULT 0, + total_tokens_saved INTEGER DEFAULT 0, + metadata JSONB DEFAULT '{}' + )`, + + // ─── audit_log (immutable, append-only) ─────────────────────────────── + `CREATE TABLE IF NOT EXISTS audit_log ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + actor TEXT NOT NULL, + action TEXT NOT NULL, + resource TEXT NOT NULL, + details JSONB DEFAULT '{}', + ip_address TEXT, + timestamp TIMESTAMPTZ DEFAULT NOW() + )`, + + // ─── provider_pricing ───────────────────────────────────────────────── + `CREATE TABLE IF NOT EXISTS provider_pricing ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + provider TEXT NOT NULL, + model TEXT NOT NULL, + input_per_mtok NUMERIC(10,6) NOT NULL, + output_per_mtok NUMERIC(10,6) NOT NULL, + cached_per_mtok NUMERIC(10,6), + effective_from DATE NOT NULL DEFAULT CURRENT_DATE, + UNIQUE(provider, model, effective_from) + )`, + + // ─── budgets ────────────────────────────────────────────────────────── + `CREATE TABLE IF NOT EXISTS budgets ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + scope_type TEXT NOT NULL, + scope_value TEXT NOT NULL, + limit_usd NUMERIC(12,4) NOT NULL, + period TEXT NOT NULL DEFAULT 'monthly', + current_spend NUMERIC(12,8) DEFAULT 0, + alert_threshold NUMERIC(3,2) DEFAULT 0.80, + hard_limit BOOLEAN DEFAULT false, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() + )`, + + // ─── Indexes ────────────────────────────────────────────────────────── + `CREATE INDEX IF NOT EXISTS idx_tickets_session ON tickets(session_id)`, + `CREATE INDEX IF NOT EXISTS idx_tickets_provider ON tickets(provider)`, + `CREATE INDEX IF NOT EXISTS idx_tickets_project ON tickets(project)`, + `CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status)`, + `CREATE INDEX IF NOT EXISTS idx_tickets_created ON tickets(created_at DESC)`, + `CREATE INDEX IF NOT EXISTS idx_tickets_caller ON tickets(caller)`, + `CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp DESC)`, + + // ─── Seed: Provider Pricing (April 2026) ────────────────────────────── + `INSERT INTO provider_pricing (provider, model, input_per_mtok, output_per_mtok, cached_per_mtok, effective_from) + VALUES + ('anthropic', 'claude-opus-4-20250514', 15.0, 75.0, 1.50, '2026-01-01'), + ('anthropic', 'claude-sonnet-4-20250514', 3.0, 15.0, 0.30, '2026-01-01'), + ('anthropic', 'claude-haiku-3-20250630', 0.25, 1.25, 0.025, '2026-01-01'), + ('openai', 'gpt-4o', 2.50, 10.0, 1.25, '2026-01-01'), + ('openai', 'gpt-4o-mini', 0.15, 0.60, 0.075, '2026-01-01'), + ('openai', 'o1', 15.0, 60.0, NULL, '2026-01-01'), + ('google', 'gemini-2.0-flash', 0.10, 0.40, 0.025, '2026-01-01'), + ('google', 'gemini-2.5-pro', 1.25, 10.0, 0.315, '2026-01-01'), + ('mistral', 'mistral-large', 2.0, 6.0, NULL, '2026-01-01'), + ('mistral', 'mistral-small', 0.10, 0.30, NULL, '2026-01-01'), + ('groq', 'llama-3.3-70b', 0.59, 0.79, NULL, '2026-01-01'), + ('cerebras', 'llama-3.3-70b', 0.85, 1.20, NULL, '2026-01-01'), + ('ollama', 'qwen2.5:14b', 0.0, 0.0, NULL, '2026-01-01'), + ('ollama', 'llama3.3:70b', 0.0, 0.0, NULL, '2026-01-01') + ON CONFLICT (provider, model, effective_from) DO NOTHING`, +]; + +export async function runMigrations(): Promise { + logger.info('Running TokenVault DB migrations...'); + for (const sql of MIGRATIONS) { + try { + await query(sql); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + if (!msg.includes('already exists')) { + logger.error({ err, sql: sql.slice(0, 60) }, 'Migration failed'); + } + } + } + logger.info('TokenVault migrations complete'); +} diff --git a/packages/core/src/observability/logger.ts b/packages/core/src/observability/logger.ts new file mode 100644 index 0000000..a59df28 --- /dev/null +++ b/packages/core/src/observability/logger.ts @@ -0,0 +1,10 @@ +import pino from 'pino'; +import { config } from '../config.js'; + +export const logger = pino({ + name: 'tokenvault', + level: config.nodeEnv === 'production' ? 'info' : 'debug', + transport: config.nodeEnv === 'development' + ? { target: 'pino-pretty', options: { colorize: true } } + : undefined, +}); diff --git a/packages/core/src/pipeline/index.ts b/packages/core/src/pipeline/index.ts new file mode 100644 index 0000000..9887cfc --- /dev/null +++ b/packages/core/src/pipeline/index.ts @@ -0,0 +1,119 @@ +import { randomUUID } from 'node:crypto'; +import { logger } from '../observability/logger.js'; +import { resolveProvider } from '../providers/index.js'; +import { createTicket, hashContent } from '../tickets/ticket-service.js'; +import type { ChatRequest, ChatResponse, PipelineContext, ProviderName } from '../types.js'; + +/** + * TokenVault 7-Stage Pipeline + * + * 1. Validate — Schema check, RBAC, budget + * 2. Compress — AST-based input compression (Phase 2) + * 3. Cache — Semantic cache lookup (Phase 2) + * 4. Route — Select provider + model + * 5. Execute — Call provider API + * 6. Track — Create ticket, update budget + * 7. Respond — Return response with cost headers + */ +export async function executePipeline(request: ChatRequest): Promise<{ + response: ChatResponse; + ticket: { id: string; ticket_number: number; cost_usd: number; tokens_saved: number }; +}> { + const ctx: PipelineContext = { + requestId: randomUUID(), + startTime: Date.now(), + request, + }; + + try { + // ─── Stage 1: Validate ──────────────────────────────────────────── + if (!request.model || !request.messages?.length) { + throw new Error('Invalid request: model and messages required'); + } + + // ─── Stage 2: Compress (Phase 2 — placeholder) ──────────────────── + // Future: AST-based compression of code content in messages + + // ─── Stage 3: Cache Check (Phase 2 — placeholder) ───────────────── + // Future: Qdrant semantic similarity lookup + + // ─── Stage 4: Route ─────────────────────────────────────────────── + const resolved = resolveProvider(request.model); + if (!resolved) { + throw new Error(`No configured provider found for model: ${request.model}`); + } + ctx.resolvedProvider = resolved.provider; + ctx.resolvedModel = request.model; + + logger.debug({ requestId: ctx.requestId, provider: resolved.provider, model: request.model }, 'Routed request'); + + // ─── Stage 5: Execute ───────────────────────────────────────────── + const response = await resolved.adapter.chat(request); + ctx.response = response; + + // ─── Stage 6: Track ─────────────────────────────────────────────── + const inputText = request.messages.map(m => m.content).join('\n'); + const outputText = response.choices.map(c => c.message.content).join('\n'); + const cost = resolved.adapter.calculateCost( + response.usage.prompt_tokens, + response.usage.completion_tokens, + response.usage.cached_tokens ?? 0, + response.model, + ); + + const ticket = await createTicket({ + provider: resolved.provider, + model: response.model, + status: 'completed', + tokens_in: response.usage.prompt_tokens, + tokens_out: response.usage.completion_tokens, + tokens_cached: response.usage.cached_tokens ?? 0, + tokens_saved: ctx.tokensSaved ?? 0, + cost_usd: cost, + cost_saved_usd: 0, + latency_ms: response.latency_ms, + cache_hit: false, + compression_mode: ctx.compressionMode, + compression_ratio: ctx.compressionRatio, + caller: request.caller, + project: request.project, + team: request.team, + input_hash: hashContent(inputText), + output_hash: hashContent(outputText), + }); + + // ─── Stage 7: Respond ───────────────────────────────────────────── + return { + response, + ticket: { + id: ticket.id, + ticket_number: ticket.ticket_number, + cost_usd: cost, + tokens_saved: ctx.tokensSaved ?? 0, + }, + }; + } catch (err) { + const errorMsg = err instanceof Error ? err.message : String(err); + logger.error({ requestId: ctx.requestId, error: errorMsg }, 'Pipeline failed'); + + // Track failed request as ticket + try { + await createTicket({ + provider: (ctx.resolvedProvider ?? 'ollama') as ProviderName, + model: request.model, + status: 'failed', + tokens_in: 0, + tokens_out: 0, + cost_usd: 0, + latency_ms: Date.now() - ctx.startTime, + caller: request.caller, + project: request.project, + team: request.team, + input_hash: hashContent(request.messages.map(m => m.content).join('\n')), + output_hash: '', + }); + } catch { /* best effort */ } + + throw err; + } +} diff --git a/packages/core/src/providers/index.ts b/packages/core/src/providers/index.ts new file mode 100644 index 0000000..205797c --- /dev/null +++ b/packages/core/src/providers/index.ts @@ -0,0 +1,275 @@ +import { config } from '../config.js'; +import { logger } from '../observability/logger.js'; +import type { ProviderAdapter, ProviderName, ChatRequest, ChatResponse, ProviderModel } from '../types.js'; +import { createHash, randomUUID } from 'node:crypto'; + +// ─── Provider Pricing (hardcoded MVP, later from DB) ──────────────────────── + +const PRICING: Record> = { + anthropic: { + 'claude-opus-4-20250514': { input: 15.0, output: 75.0, cached: 1.50 }, + 'claude-sonnet-4-20250514': { input: 3.0, output: 15.0, cached: 0.30 }, + 'claude-haiku-3-20250630': { input: 0.25, output: 1.25, cached: 0.025 }, + }, + openai: { + 'gpt-4o': { input: 2.50, output: 10.0, cached: 1.25 }, + 'gpt-4o-mini': { input: 0.15, output: 0.60, cached: 0.075 }, + 'o1': { input: 15.0, output: 60.0 }, + }, + ollama: {}, +}; + +function calcCost(provider: string, model: string, tokensIn: number, tokensOut: number, tokensCached: number): number { + const pricing = PRICING[provider]?.[model]; + if (!pricing) return 0; + const inputCost = ((tokensIn - tokensCached) / 1_000_000) * pricing.input; + const outputCost = (tokensOut / 1_000_000) * pricing.output; + const cacheCost = pricing.cached ? (tokensCached / 1_000_000) * pricing.cached : 0; + return Math.max(0, inputCost + outputCost + cacheCost); +} + +// ─── Anthropic Adapter ────────────────────────────────────────────────────── + +function createAnthropicAdapter(): ProviderAdapter { + const apiKey = config.providers.anthropic.apiKey; + const models: ProviderModel[] = [ + { id: 'claude-opus-4-20250514', displayName: 'Claude Opus 4', contextLength: 200_000, inputPricePerMTok: 15.0, outputPricePerMTok: 75.0, cachedPricePerMTok: 1.50, tier: 'premium' }, + { id: 'claude-sonnet-4-20250514', displayName: 'Claude Sonnet 4', contextLength: 200_000, inputPricePerMTok: 3.0, outputPricePerMTok: 15.0, cachedPricePerMTok: 0.30, tier: 'standard' }, + { id: 'claude-haiku-3-20250630', displayName: 'Claude Haiku 3', contextLength: 200_000, inputPricePerMTok: 0.25, outputPricePerMTok: 1.25, cachedPricePerMTok: 0.025, tier: 'fast' }, + ]; + + return { + name: 'anthropic', + displayName: 'Anthropic Claude', + models, + isConfigured: () => apiKey.length > 0, + supportsPromptCaching: () => true, + calculateCost: (tIn, tOut, tCached, model) => calcCost('anthropic', model, tIn, tOut, tCached), + async chat(request: ChatRequest): Promise { + const start = Date.now(); + const systemMsg = request.messages.find(m => m.role === 'system'); + const userMsgs = request.messages.filter(m => m.role !== 'system'); + + const body = { + model: request.model, + max_tokens: request.max_tokens ?? 4096, + temperature: request.temperature ?? 0.7, + ...(systemMsg ? { system: systemMsg.content } : {}), + messages: userMsgs.map(m => ({ role: m.role, content: m.content })), + }; + + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + }, + body: JSON.stringify(body), + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`Anthropic API error ${res.status}: ${errText.slice(0, 200)}`); + } + + const data = await res.json() as { + id: string; + model: string; + content: Array<{ type: string; text: string }>; + stop_reason: string; + usage: { input_tokens: number; output_tokens: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number }; + }; + + const cachedTokens = (data.usage.cache_read_input_tokens ?? 0); + + return { + id: data.id, + model: data.model, + provider: 'anthropic', + choices: [{ + index: 0, + message: { role: 'assistant', content: data.content.map(c => c.text).join('') }, + finish_reason: data.stop_reason, + }], + usage: { + prompt_tokens: data.usage.input_tokens, + completion_tokens: data.usage.output_tokens, + total_tokens: data.usage.input_tokens + data.usage.output_tokens, + cached_tokens: cachedTokens, + }, + latency_ms: Date.now() - start, + }; + }, + }; +} + +// ─── OpenAI Adapter ───────────────────────────────────────────────────────── + +function createOpenAIAdapter(): ProviderAdapter { + const apiKey = config.providers.openai.apiKey; + const models: ProviderModel[] = [ + { id: 'gpt-4o', displayName: 'GPT-4o', contextLength: 128_000, inputPricePerMTok: 2.50, outputPricePerMTok: 10.0, cachedPricePerMTok: 1.25, tier: 'standard' }, + { id: 'gpt-4o-mini', displayName: 'GPT-4o Mini', contextLength: 128_000, inputPricePerMTok: 0.15, outputPricePerMTok: 0.60, cachedPricePerMTok: 0.075, tier: 'fast' }, + { id: 'o1', displayName: 'o1', contextLength: 200_000, inputPricePerMTok: 15.0, outputPricePerMTok: 60.0, tier: 'reasoning' }, + ]; + + return { + name: 'openai', + displayName: 'OpenAI', + models, + isConfigured: () => apiKey.length > 0, + supportsPromptCaching: () => true, + calculateCost: (tIn, tOut, tCached, model) => calcCost('openai', model, tIn, tOut, tCached), + async chat(request: ChatRequest): Promise { + const start = Date.now(); + const res = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + model: request.model, + messages: request.messages.map(m => ({ role: m.role, content: m.content })), + temperature: request.temperature ?? 0.7, + max_tokens: request.max_tokens ?? 4096, + }), + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`OpenAI API error ${res.status}: ${errText.slice(0, 200)}`); + } + + const data = await res.json() as { + id: string; + model: string; + choices: Array<{ index: number; message: { role: string; content: string }; finish_reason: string }>; + usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number; prompt_tokens_details?: { cached_tokens?: number } }; + }; + + return { + id: data.id, + model: data.model, + provider: 'openai', + choices: data.choices.map(c => ({ + index: c.index, + message: { role: c.message.role as 'assistant', content: c.message.content }, + finish_reason: c.finish_reason, + })), + usage: { + prompt_tokens: data.usage.prompt_tokens, + completion_tokens: data.usage.completion_tokens, + total_tokens: data.usage.total_tokens, + cached_tokens: data.usage.prompt_tokens_details?.cached_tokens ?? 0, + }, + latency_ms: Date.now() - start, + }; + }, + }; +} + +// ─── Ollama Adapter ───────────────────────────────────────────────────────── + +function createOllamaAdapter(): ProviderAdapter { + const baseUrl = config.providers.ollama.url; + const models: ProviderModel[] = [ + { id: 'qwen2.5:14b', displayName: 'Qwen 2.5 14B', contextLength: 32_768, inputPricePerMTok: 0, outputPricePerMTok: 0, tier: 'standard' }, + { id: 'qwen2.5:3b', displayName: 'Qwen 2.5 3B', contextLength: 32_768, inputPricePerMTok: 0, outputPricePerMTok: 0, tier: 'fast' }, + { id: 'llama3.3:70b', displayName: 'Llama 3.3 70B', contextLength: 131_072, inputPricePerMTok: 0, outputPricePerMTok: 0, tier: 'premium' }, + ]; + + return { + name: 'ollama', + displayName: 'Ollama (Local)', + models, + isConfigured: () => true, + supportsPromptCaching: () => false, + calculateCost: () => 0, + async chat(request: ChatRequest): Promise { + const start = Date.now(); + const res = await fetch(`${baseUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: request.model, + messages: request.messages.map(m => ({ role: m.role, content: m.content })), + stream: false, + options: { + temperature: request.temperature ?? 0.7, + num_predict: request.max_tokens ?? 4096, + }, + }), + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`Ollama error ${res.status}: ${errText.slice(0, 200)}`); + } + + const data = await res.json() as { + model: string; + message: { role: string; content: string }; + done_reason: string; + prompt_eval_count?: number; + eval_count?: number; + }; + + const promptTokens = data.prompt_eval_count ?? 0; + const completionTokens = data.eval_count ?? 0; + + return { + id: randomUUID(), + model: data.model, + provider: 'ollama', + choices: [{ + index: 0, + message: { role: 'assistant', content: data.message.content }, + finish_reason: data.done_reason ?? 'stop', + }], + usage: { + prompt_tokens: promptTokens, + completion_tokens: completionTokens, + total_tokens: promptTokens + completionTokens, + }, + latency_ms: Date.now() - start, + }; + }, + }; +} + +// ─── Provider Registry ────────────────────────────────────────────────────── + +const adapters: Map = new Map(); + +export function initProviders(): void { + const anthropic = createAnthropicAdapter(); + const openai = createOpenAIAdapter(); + const ollama = createOllamaAdapter(); + + adapters.set('anthropic', anthropic); + adapters.set('openai', openai); + adapters.set('ollama', ollama); + + const configured = [...adapters.values()].filter(a => a.isConfigured()).map(a => a.name); + logger.info({ configured }, 'Provider registry initialized'); +} + +export function getProvider(name: ProviderName): ProviderAdapter | undefined { + return adapters.get(name); +} + +export function getAllProviders(): ProviderAdapter[] { + return [...adapters.values()]; +} + +export function resolveProvider(model: string): { provider: ProviderName; adapter: ProviderAdapter } | undefined { + for (const [name, adapter] of adapters) { + if (!adapter.isConfigured()) continue; + if (adapter.models.some(m => m.id === model)) { + return { provider: name, adapter }; + } + } + return undefined; +} diff --git a/packages/core/src/routes/health.ts b/packages/core/src/routes/health.ts new file mode 100644 index 0000000..dd31a4b --- /dev/null +++ b/packages/core/src/routes/health.ts @@ -0,0 +1,19 @@ +import type { FastifyInstance } from 'fastify'; +import { getAllProviders } from '../providers/index.js'; + +export async function healthRoutes(app: FastifyInstance): Promise { + app.get('/health', async () => { + const providers = getAllProviders(); + return { + status: 'ok', + service: 'tokenvault', + version: '0.1.0', + uptime_s: Math.floor(process.uptime()), + providers: providers.map(p => ({ + name: p.name, + configured: p.isConfigured(), + models: p.models.length, + })), + }; + }); +} diff --git a/packages/core/src/routes/proxy.ts b/packages/core/src/routes/proxy.ts new file mode 100644 index 0000000..210952d --- /dev/null +++ b/packages/core/src/routes/proxy.ts @@ -0,0 +1,69 @@ +import type { FastifyInstance } from 'fastify'; +import { executePipeline } from '../pipeline/index.js'; +import type { ChatRequest } from '../types.js'; + +interface ProxyBody { + model: string; + messages: Array<{ role: string; content: string }>; + temperature?: number; + max_tokens?: number; + stream?: boolean; + // TokenVault extensions + caller?: string; + project?: string; + team?: string; +} + +export async function proxyRoutes(app: FastifyInstance): Promise { + app.post<{ Body: ProxyBody }>('/v1/chat/completions', async (req, reply) => { + const { model, messages, temperature, max_tokens, caller, project, team } = req.body; + + const chatRequest: ChatRequest = { + model, + messages: messages.map(m => ({ + role: m.role as 'system' | 'user' | 'assistant' | 'tool', + content: m.content, + })), + temperature, + max_tokens, + caller: caller ?? req.headers['x-tokenvault-caller'] as string, + project: project ?? req.headers['x-tokenvault-project'] as string, + team: team ?? req.headers['x-tokenvault-team'] as string, + }; + + const { response, ticket } = await executePipeline(chatRequest); + + reply.header('X-TokenVault-Ticket-ID', ticket.id); + reply.header('X-TokenVault-Ticket-Number', `TV-${String(ticket.ticket_number).padStart(5, '0')}`); + reply.header('X-TokenVault-Cost-USD', ticket.cost_usd.toFixed(6)); + reply.header('X-TokenVault-Tokens-Saved', ticket.tokens_saved); + + // Return OpenAI-compatible format + return { + id: response.id, + object: 'chat.completion', + created: Math.floor(Date.now() / 1000), + model: response.model, + choices: response.choices.map(c => ({ + index: c.index, + message: { role: c.message.role, content: c.message.content }, + finish_reason: c.finish_reason, + })), + usage: { + prompt_tokens: response.usage.prompt_tokens, + completion_tokens: response.usage.completion_tokens, + total_tokens: response.usage.total_tokens, + cached_tokens: response.usage.cached_tokens, + }, + // TokenVault extensions + tokenvault: { + ticket_id: ticket.id, + ticket_number: `TV-${String(ticket.ticket_number).padStart(5, '0')}`, + provider: response.provider, + cost_usd: ticket.cost_usd, + tokens_saved: ticket.tokens_saved, + latency_ms: response.latency_ms, + }, + }; + }); +} diff --git a/packages/core/src/routes/tickets.ts b/packages/core/src/routes/tickets.ts new file mode 100644 index 0000000..abd80f6 --- /dev/null +++ b/packages/core/src/routes/tickets.ts @@ -0,0 +1,57 @@ +import type { FastifyInstance } from 'fastify'; +import { listTickets, getTicket, getTicketStats, getCostBreakdown } from '../tickets/ticket-service.js'; + +export async function ticketRoutes(app: FastifyInstance): Promise { + app.get<{ + Querystring: { + provider?: string; model?: string; status?: string; + caller?: string; project?: string; team?: string; + from?: string; to?: string; + limit?: string; offset?: string; + }; + }>('/v1/tickets', async (req) => { + const { tickets, total } = await listTickets({ + provider: req.query.provider as never, + model: req.query.model, + status: req.query.status as never, + caller: req.query.caller, + project: req.query.project, + team: req.query.team, + from: req.query.from ? new Date(req.query.from) : undefined, + to: req.query.to ? new Date(req.query.to) : undefined, + limit: req.query.limit ? parseInt(req.query.limit, 10) : undefined, + offset: req.query.offset ? parseInt(req.query.offset, 10) : undefined, + }); + + return { + tickets: tickets.map(t => ({ + ...t, + ticket_display: `TV-${String(t.ticket_number).padStart(5, '0')}`, + })), + total, + limit: parseInt(req.query.limit ?? '50', 10), + offset: parseInt(req.query.offset ?? '0', 10), + }; + }); + + app.get<{ Params: { id: string } }>('/v1/tickets/:id', async (req, reply) => { + const ticket = await getTicket(req.params.id); + if (!ticket) { + reply.code(404); + return { error: 'Ticket not found' }; + } + return { ...ticket, ticket_display: `TV-${String(ticket.ticket_number).padStart(5, '0')}` }; + }); + + app.get<{ Querystring: { period?: string } }>('/v1/tickets/stats', async (req) => { + return getTicketStats(req.query.period ?? 'today'); + }); + + app.get<{ Querystring: { period?: string } }>('/v1/cost', async (req) => { + return getTicketStats(req.query.period ?? 'month'); + }); + + app.get<{ Querystring: { group_by?: string } }>('/v1/cost/breakdown', async (req) => { + return getCostBreakdown(req.query.group_by ?? 'provider'); + }); +} diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts new file mode 100644 index 0000000..20e460c --- /dev/null +++ b/packages/core/src/server.ts @@ -0,0 +1,53 @@ +import Fastify from 'fastify'; +import cors from '@fastify/cors'; +import { config } from './config.js'; +import { logger } from './observability/logger.js'; +import { runMigrations } from './db/migrate.js'; +import { closePool } from './db/client.js'; +import { initProviders } from './providers/index.js'; +import { healthRoutes } from './routes/health.js'; +import { proxyRoutes } from './routes/proxy.js'; +import { ticketRoutes } from './routes/tickets.js'; + +const app = Fastify({ + logger: false, +}); + +await app.register(cors, { origin: true }); + +// ─── Routes ────────────────────────────────────────────────────────────────── +await app.register(healthRoutes); +await app.register(proxyRoutes); +await app.register(ticketRoutes); + +// ─── Startup ───────────────────────────────────────────────────────────────── +async function startup(): Promise { + logger.info('TokenVault starting...'); + + try { + await runMigrations(); + } catch (err) { + logger.error({ err }, 'DB migration failed — proceeding in degraded mode'); + } + + initProviders(); + + await app.listen({ port: config.port, host: config.host }); + logger.info({ port: config.port, host: config.host }, 'TokenVault proxy server running'); +} + +// ─── Graceful Shutdown ─────────────────────────────────────────────────────── +async function shutdown(signal: string): Promise { + logger.info({ signal }, 'TokenVault shutting down'); + await app.close(); + await closePool(); + process.exit(0); +} + +process.on('SIGINT', () => void shutdown('SIGINT')); +process.on('SIGTERM', () => void shutdown('SIGTERM')); + +startup().catch((err) => { + logger.fatal({ err }, 'TokenVault startup failed'); + process.exit(1); +}); diff --git a/packages/core/src/tickets/ticket-service.ts b/packages/core/src/tickets/ticket-service.ts new file mode 100644 index 0000000..911b8f8 --- /dev/null +++ b/packages/core/src/tickets/ticket-service.ts @@ -0,0 +1,157 @@ +import { createHash } from 'node:crypto'; +import { query } from '../db/client.js'; +import { logger } from '../observability/logger.js'; +import type { Ticket, TicketFilter, TicketStatus, ProviderName, CostSummary, CostBreakdown } from '../types.js'; + +export async function createTicket(data: { + session_id?: string; + provider: ProviderName; + model: string; + status: TicketStatus; + tokens_in: number; + tokens_out: number; + tokens_cached?: number; + tokens_saved?: number; + cost_usd: number; + cost_saved_usd?: number; + latency_ms: number; + cache_hit?: boolean; + compression_mode?: string; + compression_ratio?: number; + caller?: string; + project?: string; + team?: string; + input_hash?: string; + output_hash?: string; +}): Promise { + const result = await query( + `INSERT INTO tickets ( + session_id, provider, model, status, + tokens_in, tokens_out, tokens_cached, tokens_saved, + cost_usd, cost_saved_usd, latency_ms, cache_hit, + compression_mode, compression_ratio, + caller, project, team, input_hash, output_hash + ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19) + RETURNING *`, + [ + data.session_id ?? null, data.provider, data.model, data.status, + data.tokens_in, data.tokens_out, data.tokens_cached ?? 0, data.tokens_saved ?? 0, + data.cost_usd, data.cost_saved_usd ?? 0, data.latency_ms, data.cache_hit ?? false, + data.compression_mode ?? null, data.compression_ratio ?? null, + data.caller ?? null, data.project ?? null, data.team ?? null, + data.input_hash ?? null, data.output_hash ?? null, + ], + ); + logger.info({ ticket_number: result.rows[0]?.ticket_number, provider: data.provider, model: data.model, cost: data.cost_usd }, 'Ticket created'); + return result.rows[0]!; +} + +export async function getTicket(id: string): Promise { + const result = await query('SELECT * FROM tickets WHERE id = $1', [id]); + return result.rows[0] ?? null; +} + +export async function listTickets(filter: TicketFilter = {}): Promise<{ tickets: Ticket[]; total: number }> { + const conditions: string[] = []; + const params: unknown[] = []; + let idx = 1; + + if (filter.provider) { conditions.push(`provider = $${idx++}`); params.push(filter.provider); } + if (filter.model) { conditions.push(`model = $${idx++}`); params.push(filter.model); } + if (filter.status) { conditions.push(`status = $${idx++}`); params.push(filter.status); } + if (filter.caller) { conditions.push(`caller = $${idx++}`); params.push(filter.caller); } + if (filter.project) { conditions.push(`project = $${idx++}`); params.push(filter.project); } + if (filter.team) { conditions.push(`team = $${idx++}`); params.push(filter.team); } + if (filter.from) { conditions.push(`created_at >= $${idx++}`); params.push(filter.from); } + if (filter.to) { conditions.push(`created_at <= $${idx++}`); params.push(filter.to); } + + const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; + const limit = filter.limit ?? 50; + const offset = filter.offset ?? 0; + + const [tickets, countResult] = await Promise.all([ + query(`SELECT * FROM tickets ${where} ORDER BY created_at DESC LIMIT $${idx++} OFFSET $${idx++}`, [...params, limit, offset]), + query<{ count: string }>(`SELECT COUNT(*) as count FROM tickets ${where}`, params), + ]); + + return { + tickets: tickets.rows, + total: parseInt(countResult.rows[0]?.count ?? '0', 10), + }; +} + +export async function getTicketStats(period: string = 'today'): Promise { + const intervals: Record = { + today: "created_at >= CURRENT_DATE", + week: "created_at >= CURRENT_DATE - INTERVAL '7 days'", + month: "created_at >= CURRENT_DATE - INTERVAL '30 days'", + all: '1=1', + }; + const where = intervals[period] ?? intervals['today']!; + + const result = await query<{ + total_cost: string; total_saved: string; + total_in: string; total_out: string; total_cached: string; total_saved_tokens: string; + total_requests: string; cache_hits: string; avg_ratio: string; + }>(`SELECT + COALESCE(SUM(cost_usd), 0) as total_cost, + COALESCE(SUM(cost_saved_usd), 0) as total_saved, + COALESCE(SUM(tokens_in), 0) as total_in, + COALESCE(SUM(tokens_out), 0) as total_out, + COALESCE(SUM(tokens_cached), 0) as total_cached, + COALESCE(SUM(tokens_saved), 0) as total_saved_tokens, + COUNT(*) as total_requests, + COUNT(*) FILTER (WHERE cache_hit = true) as cache_hits, + COALESCE(AVG(compression_ratio) FILTER (WHERE compression_ratio IS NOT NULL), 0) as avg_ratio + FROM tickets WHERE ${where}`); + + const row = result.rows[0]!; + const totalReqs = parseInt(row.total_requests, 10); + const cacheHits = parseInt(row.cache_hits, 10); + + return { + period, + total_cost_usd: parseFloat(row.total_cost), + total_saved_usd: parseFloat(row.total_saved), + total_tokens_in: parseInt(row.total_in, 10), + total_tokens_out: parseInt(row.total_out, 10), + total_tokens_cached: parseInt(row.total_cached, 10), + total_tokens_saved: parseInt(row.total_saved_tokens, 10), + total_requests: totalReqs, + cache_hit_rate: totalReqs > 0 ? cacheHits / totalReqs : 0, + avg_compression_ratio: parseFloat(row.avg_ratio), + }; +} + +export async function getCostBreakdown(groupBy: string = 'provider'): Promise { + const validGroups = ['provider', 'model', 'project', 'team', 'caller']; + const col = validGroups.includes(groupBy) ? groupBy : 'provider'; + + const result = await query<{ + group_value: string; cost: string; saved: string; count: string; t_in: string; t_out: string; + }>(`SELECT + COALESCE(${col}, 'unknown') as group_value, + COALESCE(SUM(cost_usd), 0) as cost, + COALESCE(SUM(cost_saved_usd), 0) as saved, + COUNT(*) as count, + COALESCE(SUM(tokens_in), 0) as t_in, + COALESCE(SUM(tokens_out), 0) as t_out + FROM tickets + WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' + GROUP BY ${col} + ORDER BY cost DESC`); + + return result.rows.map(r => ({ + group_by: groupBy, + group_value: r.group_value, + cost_usd: parseFloat(r.cost), + saved_usd: parseFloat(r.saved), + request_count: parseInt(r.count, 10), + tokens_in: parseInt(r.t_in, 10), + tokens_out: parseInt(r.t_out, 10), + })); +} + +export function hashContent(content: string): string { + return createHash('sha256').update(content).digest('hex').slice(0, 16); +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000..31ad9da --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,151 @@ +// ─── Provider Types ──────────────────────────────────────────────────────── + +export type ProviderName = 'anthropic' | 'openai' | 'google' | 'mistral' | 'groq' | 'cerebras' | 'ollama'; + +export type TicketStatus = 'completed' | 'cached' | 'failed' | 'pending_review'; + +export type ModelTier = 'fast' | 'standard' | 'premium' | 'reasoning'; + +export interface ProviderModel { + readonly id: string; + readonly displayName: string; + readonly contextLength: number; + readonly inputPricePerMTok: number; + readonly outputPricePerMTok: number; + readonly cachedPricePerMTok?: number; + readonly tier: ModelTier; +} + +export interface ProviderAdapter { + readonly name: ProviderName; + readonly displayName: string; + readonly models: readonly ProviderModel[]; + isConfigured(): boolean; + chat(request: ChatRequest): Promise; + supportsPromptCaching(): boolean; + calculateCost(tokensIn: number, tokensOut: number, tokensCached: number, model: string): number; +} + +// ─── Chat Types (OpenAI-compatible) ──────────────────────────────────────── + +export interface ChatMessage { + readonly role: 'system' | 'user' | 'assistant' | 'tool'; + readonly content: string; + readonly name?: string; +} + +export interface ChatRequest { + readonly model: string; + readonly messages: readonly ChatMessage[]; + readonly temperature?: number; + readonly max_tokens?: number; + readonly stream?: boolean; + readonly caller?: string; + readonly project?: string; + readonly team?: string; +} + +export interface ChatResponse { + readonly id: string; + readonly model: string; + readonly provider: ProviderName; + readonly choices: readonly ChatChoice[]; + readonly usage: TokenUsage; + readonly latency_ms: number; +} + +export interface ChatChoice { + readonly index: number; + readonly message: ChatMessage; + readonly finish_reason: string; +} + +export interface TokenUsage { + readonly prompt_tokens: number; + readonly completion_tokens: number; + readonly total_tokens: number; + readonly cached_tokens?: number; +} + +// ─── Ticket Types ────────────────────────────────────────────────────────── + +export interface Ticket { + readonly id: string; + readonly ticket_number: number; + readonly session_id: string | null; + readonly provider: ProviderName; + readonly model: string; + readonly status: TicketStatus; + readonly tokens_in: number; + readonly tokens_out: number; + readonly tokens_cached: number; + readonly tokens_saved: number; + readonly cost_usd: number; + readonly cost_saved_usd: number; + readonly latency_ms: number; + readonly cache_hit: boolean; + readonly compression_mode: string | null; + readonly compression_ratio: number | null; + readonly caller: string | null; + readonly project: string | null; + readonly team: string | null; + readonly input_hash: string; + readonly output_hash: string; + readonly created_at: Date; +} + +export interface TicketFilter { + readonly provider?: ProviderName; + readonly model?: string; + readonly status?: TicketStatus; + readonly caller?: string; + readonly project?: string; + readonly team?: string; + readonly from?: Date; + readonly to?: Date; + readonly limit?: number; + readonly offset?: number; +} + +// ─── Cost Types ──────────────────────────────────────────────────────────── + +export interface CostSummary { + readonly period: string; + readonly total_cost_usd: number; + readonly total_saved_usd: number; + readonly total_tokens_in: number; + readonly total_tokens_out: number; + readonly total_tokens_cached: number; + readonly total_tokens_saved: number; + readonly total_requests: number; + readonly cache_hit_rate: number; + readonly avg_compression_ratio: number; +} + +export interface CostBreakdown { + readonly group_by: string; + readonly group_value: string; + readonly cost_usd: number; + readonly saved_usd: number; + readonly request_count: number; + readonly tokens_in: number; + readonly tokens_out: number; +} + +// ─── Pipeline Types ──────────────────────────────────────────────────────── + +export interface PipelineContext { + readonly requestId: string; + readonly startTime: number; + request: ChatRequest; + resolvedProvider?: ProviderName; + resolvedModel?: string; + cacheHit?: boolean; + cachedResponse?: ChatResponse; + compressionMode?: string; + compressionRatio?: number; + tokensSaved?: number; + response?: ChatResponse; + ticket?: Ticket; + error?: string; +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..a086b14 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts new file mode 100644 index 0000000..4676a92 --- /dev/null +++ b/packages/core/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/**/*.ts'], + format: ['esm'], + target: 'node20', + outDir: 'dist', + clean: true, + sourcemap: true, + dts: true, + splitting: false, + bundle: false, + skipNodeModulesBundle: true, +}); diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json new file mode 100644 index 0000000..14caf97 --- /dev/null +++ b/packages/dashboard/package.json @@ -0,0 +1,25 @@ +{ + "name": "@tokenvault/dashboard", + "version": "0.1.0", + "description": "TokenVault web dashboard — cost tracking, ticket browser, provider analytics", + "type": "module", + "main": "dist/server.js", + "license": "Apache-2.0", + "scripts": { + "build": "tsup", + "dev": "tsx watch src/server.ts", + "test": "vitest run", + "clean": "rm -rf dist" + }, + "dependencies": { + "fastify": "^5.3.0", + "@fastify/static": "^8.1.0" + }, + "devDependencies": { + "tsup": "^8.4.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^3.1.0", + "@types/node": "^22.0.0" + } +} diff --git a/packages/dashboard/public/index.html b/packages/dashboard/public/index.html new file mode 100644 index 0000000..a6a6423 --- /dev/null +++ b/packages/dashboard/public/index.html @@ -0,0 +1,286 @@ + + + + + +TokenVault — LLM Cost Intelligence + + + + +
+

TokenVault v0.1.0

+
+
+ + +
+ +
+
+ +
+
Overview
+
Tickets
+
Cost Analysis
+
Providers
+
+ +
+ +
+
+
Loading...
+
+
+
Cost timeline will appear here after requests are tracked
+
Provider split will appear here
+
+
+ + + + + + + + + +
+ + + + diff --git a/packages/dashboard/src/server.ts b/packages/dashboard/src/server.ts new file mode 100644 index 0000000..ce789dc --- /dev/null +++ b/packages/dashboard/src/server.ts @@ -0,0 +1,51 @@ +import Fastify from 'fastify'; +import fastifyStatic from '@fastify/static'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const PORT = parseInt(process.env['PORT'] ?? '3301', 10); +const CORE_URL = process.env['TOKENVAULT_CORE_URL'] ?? 'http://localhost:3300'; + +const app = Fastify({ logger: false }); + +// Serve static dashboard HTML +await app.register(fastifyStatic, { + root: join(__dirname, '..', 'public'), + prefix: '/', +}); + +// Proxy API requests to core +app.get('/api/health', async () => { + const res = await fetch(`${CORE_URL}/health`); + return res.json(); +}); + +app.get('/api/tickets', async (req) => { + const qs = new URL(req.url, 'http://localhost').search; + const res = await fetch(`${CORE_URL}/v1/tickets${qs}`); + return res.json(); +}); + +app.get('/api/tickets/stats', async (req) => { + const qs = new URL(req.url, 'http://localhost').search; + const res = await fetch(`${CORE_URL}/v1/tickets/stats${qs}`); + return res.json(); +}); + +app.get('/api/cost', async (req) => { + const qs = new URL(req.url, 'http://localhost').search; + const res = await fetch(`${CORE_URL}/v1/cost${qs}`); + return res.json(); +}); + +app.get('/api/cost/breakdown', async (req) => { + const qs = new URL(req.url, 'http://localhost').search; + const res = await fetch(`${CORE_URL}/v1/cost/breakdown${qs}`); + return res.json(); +}); + +await app.listen({ port: PORT, host: '0.0.0.0' }); +console.log(`TokenVault Dashboard running on http://localhost:${PORT}`); diff --git a/packages/dashboard/tsconfig.json b/packages/dashboard/tsconfig.json new file mode 100644 index 0000000..a086b14 --- /dev/null +++ b/packages/dashboard/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/dashboard/tsup.config.ts b/packages/dashboard/tsup.config.ts new file mode 100644 index 0000000..4676a92 --- /dev/null +++ b/packages/dashboard/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/**/*.ts'], + format: ['esm'], + target: 'node20', + outDir: 'dist', + clean: true, + sourcemap: true, + dts: true, + splitting: false, + bundle: false, + skipNodeModulesBundle: true, +}); diff --git a/packages/mcp/package.json b/packages/mcp/package.json new file mode 100644 index 0000000..e8d2fb7 --- /dev/null +++ b/packages/mcp/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tokenvault/mcp", + "version": "0.1.0", + "description": "TokenVault MCP server for IDE integration — compressed reads, ticket view, cost dashboard", + "type": "module", + "main": "dist/server.js", + "bin": { + "tokenvault-mcp": "dist/server.js" + }, + "license": "Apache-2.0", + "scripts": { + "build": "tsup", + "dev": "tsx watch src/server.ts", + "test": "vitest run", + "clean": "rm -rf dist" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.27.0", + "zod": "^3.24.0" + }, + "devDependencies": { + "tsup": "^8.4.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^3.1.0", + "@types/node": "^22.0.0" + } +} diff --git a/packages/mcp/src/server.ts b/packages/mcp/src/server.ts new file mode 100644 index 0000000..f187015 --- /dev/null +++ b/packages/mcp/src/server.ts @@ -0,0 +1,85 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { z } from 'zod'; + +const CORE_URL = process.env['TOKENVAULT_URL'] ?? 'http://localhost:3300'; + +async function fetchCore(path: string): Promise { + const res = await fetch(`${CORE_URL}${path}`); + if (!res.ok) throw new Error(`TokenVault API error: ${res.status}`); + return res.json() as Promise; +} + +const server = new McpServer({ + name: 'tokenvault', + version: '0.1.0', +}); + +// ─── tv_ticket: View and search tickets ────────────────────────────────────── +server.tool( + 'tv_ticket', + 'View, search, and manage TokenVault tickets. Every LLM request = 1 ticket.', + { + action: z.enum(['list', 'get', 'stats']).describe('Action to perform'), + id: z.string().optional().describe('Ticket ID (for get action)'), + provider: z.string().optional().describe('Filter by provider'), + project: z.string().optional().describe('Filter by project'), + period: z.string().optional().describe('Stats period: today, week, month, all'), + limit: z.number().optional().describe('Max results (default 20)'), + }, + async ({ action, id, provider, project, period, limit }) => { + if (action === 'get' && id) { + const ticket = await fetchCore(`/v1/tickets/${id}`); + return { content: [{ type: 'text' as const, text: JSON.stringify(ticket, null, 2) }] }; + } + + if (action === 'stats') { + const stats = await fetchCore(`/v1/tickets/stats?period=${period ?? 'today'}`); + return { content: [{ type: 'text' as const, text: JSON.stringify(stats, null, 2) }] }; + } + + const params = new URLSearchParams(); + if (provider) params.set('provider', provider); + if (project) params.set('project', project); + params.set('limit', String(limit ?? 20)); + const data = await fetchCore(`/v1/tickets?${params}`); + return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] }; + }, +); + +// ─── tv_cost: Cost dashboard ───────────────────────────────────────────────── +server.tool( + 'tv_cost', + 'Show cost dashboard: spend, savings, forecasts. Tracks every token across all LLM providers.', + { + period: z.enum(['today', 'week', 'month', 'all']).optional().describe('Time period'), + group_by: z.enum(['provider', 'model', 'project', 'team']).optional().describe('Group breakdown by'), + }, + async ({ period, group_by }) => { + const [summary, breakdown] = await Promise.all([ + fetchCore(`/v1/cost?period=${period ?? 'month'}`), + group_by ? fetchCore(`/v1/cost/breakdown?group_by=${group_by}`) : Promise.resolve(null), + ]); + + const parts = [`# Cost Summary (${period ?? 'month'})\n${JSON.stringify(summary, null, 2)}`]; + if (breakdown) { + parts.push(`\n# Breakdown by ${group_by}\n${JSON.stringify(breakdown, null, 2)}`); + } + return { content: [{ type: 'text' as const, text: parts.join('\n') }] }; + }, +); + +// ─── tv_health: Service health ─────────────────────────────────────────────── +server.tool( + 'tv_health', + 'Check TokenVault service health and configured providers.', + {}, + async () => { + const health = await fetchCore('/health'); + return { content: [{ type: 'text' as const, text: JSON.stringify(health, null, 2) }] }; + }, +); + +// ─── Start ─────────────────────────────────────────────────────────────────── +const transport = new StdioServerTransport(); +await server.connect(transport); diff --git a/packages/mcp/tsconfig.json b/packages/mcp/tsconfig.json new file mode 100644 index 0000000..a086b14 --- /dev/null +++ b/packages/mcp/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/mcp/tsup.config.ts b/packages/mcp/tsup.config.ts new file mode 100644 index 0000000..5e764ba --- /dev/null +++ b/packages/mcp/tsup.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/**/*.ts'], + format: ['esm'], + target: 'node20', + outDir: 'dist', + clean: true, + sourcemap: true, + dts: true, + splitting: false, + bundle: false, + skipNodeModulesBundle: true, + banner: { js: '#!/usr/bin/env node' }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..b7bf518 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2730 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + packages/client: + dependencies: + zod: + specifier: ^3.24.0 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + tsup: + specifier: ^8.4.0 + version: 8.5.1(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^3.1.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + packages/core: + dependencies: + '@fastify/cors': + specifier: ^11.0.0 + version: 11.2.0 + fastify: + specifier: ^5.3.0 + version: 5.8.4 + nanoid: + specifier: ^5.1.0 + version: 5.1.7 + pg: + specifier: ^8.13.0 + version: 8.20.0 + pino: + specifier: ^9.6.0 + version: 9.14.0 + zod: + specifier: ^3.24.0 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + '@types/pg': + specifier: ^8.11.0 + version: 8.20.0 + tsup: + specifier: ^8.4.0 + version: 8.5.1(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^3.1.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + packages/dashboard: + dependencies: + '@fastify/static': + specifier: ^8.1.0 + version: 8.3.0 + fastify: + specifier: ^5.3.0 + version: 5.8.4 + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + tsup: + specifier: ^8.4.0 + version: 8.5.1(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^3.1.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + packages/mcp: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.27.0 + version: 1.29.0(zod@3.25.76) + zod: + specifier: ^3.24.0 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + tsup: + specifier: ^8.4.0 + version: 8.5.1(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^3.1.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + +packages: + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@fastify/accept-negotiator@2.0.1': + resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==} + + '@fastify/ajv-compiler@4.0.5': + resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} + + '@fastify/cors@11.2.0': + resolution: {integrity: sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==} + + '@fastify/error@4.2.0': + resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} + + '@fastify/fast-json-stringify-compiler@5.0.3': + resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} + + '@fastify/forwarded@3.0.1': + resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} + + '@fastify/merge-json-schemas@0.2.1': + resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} + + '@fastify/proxy-addr@5.1.0': + resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} + + '@fastify/send@4.1.0': + resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} + + '@fastify/static@8.3.0': + resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==} + + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lukeed/ms@2.0.2': + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@rollup/rollup-android-arm-eabi@4.60.1': + resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.1': + resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.1': + resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.1': + resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.1': + resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.1': + resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.1': + resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.1': + resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.1': + resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.1': + resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.1': + resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.1': + resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.1': + resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.1': + resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.1': + resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.1': + resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.1': + resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.1': + resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.1': + resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.1': + resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.1': + resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} + cpu: [x64] + os: [win32] + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@22.19.17': + resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} + + '@types/pg@8.20.0': + resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==} + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + avvio@9.2.0: + resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stringify@6.3.0: + resolution: {integrity: sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==} + + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastify-plugin@5.1.0: + resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} + + fastify@5.8.4: + resolution: {integrity: sha512-sa42J1xylbBAYUWALSBoyXKPDUvM3OoNOibIefA+Oha57FryXKKCZarA1iDntOCWp3O35voZLuDg2mdODXtPzQ==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-my-way@9.5.0: + resolution: {integrity: sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==} + engines: {node: '>=20'} + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + 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 + hasBin: true + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} + engines: {node: '>=16.9.0'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} + engines: {node: '>= 10'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + json-schema-ref-resolver@3.0.0: + resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + light-my-request@6.6.0: + resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.7: + resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} + engines: {node: ^18 || >=20} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + pg-cloudflare@1.3.0: + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + + pg-connection-string@2.12.0: + resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.13.0: + resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.13.0: + resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.20.0: + resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss@8.5.9: + resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + process-warning@4.0.1: + resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + ret@0.5.0: + resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} + engines: {node: '>=10'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.60.1: + resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex2@5.1.0: + resolution: {integrity: sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==} + hasBin: true + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + 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 + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + 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 + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@fastify/accept-negotiator@2.0.1': {} + + '@fastify/ajv-compiler@4.0.5': + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + fast-uri: 3.1.0 + + '@fastify/cors@11.2.0': + dependencies: + fastify-plugin: 5.1.0 + toad-cache: 3.7.0 + + '@fastify/error@4.2.0': {} + + '@fastify/fast-json-stringify-compiler@5.0.3': + dependencies: + fast-json-stringify: 6.3.0 + + '@fastify/forwarded@3.0.1': {} + + '@fastify/merge-json-schemas@0.2.1': + dependencies: + dequal: 2.0.3 + + '@fastify/proxy-addr@5.1.0': + dependencies: + '@fastify/forwarded': 3.0.1 + ipaddr.js: 2.3.0 + + '@fastify/send@4.1.0': + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.1 + mime: 3.0.0 + + '@fastify/static@8.3.0': + dependencies: + '@fastify/accept-negotiator': 2.0.1 + '@fastify/send': 4.1.0 + content-disposition: 0.5.4 + fastify-plugin: 5.1.0 + fastq: 1.20.1 + glob: 11.1.0 + + '@hono/node-server@1.19.14(hono@4.12.12)': + dependencies: + hono: 4.12.12 + + '@isaacs/cliui@9.0.0': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lukeed/ms@2.0.2': {} + + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.12) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.12 + jose: 6.2.2 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@pinojs/redact@0.4.0': {} + + '@rollup/rollup-android-arm-eabi@4.60.1': + optional: true + + '@rollup/rollup-android-arm64@4.60.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.1': + optional: true + + '@rollup/rollup-darwin-x64@4.60.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.1': + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/node@22.19.17': + dependencies: + undici-types: 6.21.0 + + '@types/pg@8.20.0': + dependencies: + '@types/node': 22.19.17 + pg-protocol: 1.13.0 + pg-types: 2.2.0 + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.3.2(@types/node@22.19.17)(tsx@4.21.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.2(@types/node@22.19.17)(tsx@4.21.0) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + abstract-logging@2.0.1: {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn@8.16.0: {} + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + any-promise@1.3.0: {} + + assertion-error@2.0.1: {} + + atomic-sleep@1.0.0: {} + + avvio@9.2.0: + dependencies: + '@fastify/error': 4.2.0 + fastq: 1.20.1 + + balanced-match@4.0.4: {} + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.1 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + bundle-require@5.1.0(esbuild@0.27.7): + dependencies: + esbuild: 0.27.7 + load-tsconfig: 0.2.5 + + bytes@3.1.2: {} + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + check-error@2.1.3: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + commander@4.1.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cookie@1.1.1: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escape-html@1.0.3: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + etag@1.8.1: {} + + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + + expect-type@1.3.0: {} + + express-rate-limit@8.3.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.1.0 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.1 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-decode-uri-component@1.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stringify@6.3.0: + dependencies: + '@fastify/merge-json-schemas': 0.2.1 + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + fast-uri: 3.1.0 + json-schema-ref-resolver: 3.0.0 + rfdc: 1.4.1 + + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + + fast-uri@3.1.0: {} + + fastify-plugin@5.1.0: {} + + fastify@5.8.4: + dependencies: + '@fastify/ajv-compiler': 4.0.5 + '@fastify/error': 4.2.0 + '@fastify/fast-json-stringify-compiler': 5.0.3 + '@fastify/proxy-addr': 5.1.0 + abstract-logging: 2.0.1 + avvio: 9.2.0 + fast-json-stringify: 6.3.0 + find-my-way: 9.5.0 + light-my-request: 6.6.0 + pino: 9.14.0 + process-warning: 5.0.0 + rfdc: 1.4.1 + secure-json-parse: 4.1.0 + semver: 7.7.4 + toad-cache: 3.7.0 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-my-way@9.5.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 5.1.0 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.1 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hono@4.12.12: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ip-address@10.1.0: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.3.0: {} + + is-promise@4.0.0: {} + + isexe@2.0.0: {} + + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + + jose@6.2.2: {} + + joycon@3.1.1: {} + + js-tokens@9.0.1: {} + + json-schema-ref-resolver@3.0.0: + dependencies: + dequal: 2.0.3 + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + light-my-request@6.6.0: + dependencies: + cookie: 1.1.1 + process-warning: 4.0.1 + set-cookie-parser: 2.7.2 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + loupe@3.2.1: {} + + lru-cache@11.3.5: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@3.0.0: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minipass@7.1.3: {} + + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.11: {} + + nanoid@5.1.7: {} + + negotiator@1.0.0: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + package-json-from-dist@1.0.1: {} + + parseurl@1.3.3: {} + + path-key@3.1.1: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.3.5 + minipass: 7.1.3 + + path-to-regexp@8.4.2: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + pg-cloudflare@1.3.0: + optional: true + + pg-connection-string@2.12.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.13.0(pg@8.20.0): + dependencies: + pg: 8.20.0 + + pg-protocol@1.13.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.1 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.20.0: + dependencies: + pg-connection-string: 2.12.0 + pg-pool: 3.13.0(pg@8.20.0) + pg-protocol: 1.13.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.3.0 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + + pirates@4.0.7: {} + + pkce-challenge@5.0.1: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + postcss-load-config@6.0.1(postcss@8.5.9)(tsx@4.21.0): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.9 + tsx: 4.21.0 + + postcss@8.5.9: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.1: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + process-warning@4.0.1: {} + + process-warning@5.0.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + + quick-format-unescaped@4.0.4: {} + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + readdirp@4.1.2: {} + + real-require@0.2.0: {} + + require-from-string@2.0.2: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + ret@0.5.0: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rollup@4.60.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.1 + '@rollup/rollup-android-arm64': 4.60.1 + '@rollup/rollup-darwin-arm64': 4.60.1 + '@rollup/rollup-darwin-x64': 4.60.1 + '@rollup/rollup-freebsd-arm64': 4.60.1 + '@rollup/rollup-freebsd-x64': 4.60.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 + '@rollup/rollup-linux-arm-musleabihf': 4.60.1 + '@rollup/rollup-linux-arm64-gnu': 4.60.1 + '@rollup/rollup-linux-arm64-musl': 4.60.1 + '@rollup/rollup-linux-loong64-gnu': 4.60.1 + '@rollup/rollup-linux-loong64-musl': 4.60.1 + '@rollup/rollup-linux-ppc64-gnu': 4.60.1 + '@rollup/rollup-linux-ppc64-musl': 4.60.1 + '@rollup/rollup-linux-riscv64-gnu': 4.60.1 + '@rollup/rollup-linux-riscv64-musl': 4.60.1 + '@rollup/rollup-linux-s390x-gnu': 4.60.1 + '@rollup/rollup-linux-x64-gnu': 4.60.1 + '@rollup/rollup-linux-x64-musl': 4.60.1 + '@rollup/rollup-openbsd-x64': 4.60.1 + '@rollup/rollup-openharmony-arm64': 4.60.1 + '@rollup/rollup-win32-arm64-msvc': 4.60.1 + '@rollup/rollup-win32-ia32-msvc': 4.60.1 + '@rollup/rollup-win32-x64-gnu': 4.60.1 + '@rollup/rollup-win32-x64-msvc': 4.60.1 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safe-regex2@5.1.0: + dependencies: + ret: 0.5.0 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + secure-json-parse@4.1.0: {} + + semver@7.7.4: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + set-cookie-parser@2.7.2: {} + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + split2@4.2.0: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + toad-cache@3.7.0: {} + + toidentifier@1.0.1: {} + + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + tsup@8.5.1(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.7) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.7 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.9)(tsx@4.21.0) + resolve-from: 5.0.0 + rollup: 4.60.1 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.9 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + tsx@4.21.0: + dependencies: + esbuild: 0.27.7 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typescript@5.9.3: {} + + ufo@1.6.3: {} + + undici-types@6.21.0: {} + + unpipe@1.0.0: {} + + vary@1.1.2: {} + + vite-node@3.2.4(@types/node@22.19.17)(tsx@4.21.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.2(@types/node@22.19.17)(tsx@4.21.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.3.2(@types/node@22.19.17)(tsx@4.21.0): + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.9 + rollup: 4.60.1 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 22.19.17 + fsevents: 2.3.3 + tsx: 4.21.0 + + vitest@3.2.4(@types/node@22.19.17)(tsx@4.21.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.2(@types/node@22.19.17)(tsx@4.21.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.2(@types/node@22.19.17)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.17 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrappy@1.0.2: {} + + xtend@4.0.2: {} + + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..18ec407 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..09e9336 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "Node16", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "rootDir": "src" + } +}