diff --git a/README.md b/README.md index 90448242..b3e001eb 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The hotel industry runs on closed-source, legacy PMS platforms that charge per-room fees, lock data behind proprietary APIs, and treat integrations as an afterthought. Hotels pay $5–15/room/month just for the privilege of managing their own operations. -HAIP is a **complete, production-grade hotel Property Management System** built from scratch with modern architecture. Reservation lifecycle, folio & billing, rate plans, housekeeping with digital checklists, night audit, groups & commercial (allotment + rooming lists), accounting & cashiering (A/R, deposits, cash drawers, GL codes), channel distribution to 450+ OTAs, a **full commission-free direct booking engine** (guest-facing widget + public booking API) so hotels take reservations straight from their own website, **pluggable payment gateways** (Stripe and other PSPs), Keycloak authentication, local user & role administration, media management for property and room photos, tax calculation, fiscal invoicing & guest-registration providers, an **integration registry** for adapters and providers, door-lock credential hooks, revenue management — and **12 built-in AI agents** that orchestrate revenue strategy, optimize pricing, predict cancellations, detect audit anomalies, prioritize receivables collections, forecast group pickup, schedule housekeeping, automate guest communications, and draft review responses. It even ships a **ChatGPT gateway** so guests can search and book a room by chatting. Dashboard locales include English, German, and Portuguese (BR). All open source under Apache 2.0. +HAIP is a **complete, production-grade hotel Property Management System** built from scratch with modern architecture. Reservation lifecycle, folio & billing, rate plans, housekeeping with digital checklists, night audit, groups & commercial (allotment + rooming lists), accounting & cashiering (A/R, deposits, cash drawers, GL codes), channel distribution to 450+ OTAs, a **full commission-free direct booking engine** (guest-facing widget + public booking API) so hotels take reservations straight from their own website, **pluggable payment gateways** (Stripe and other PSPs), Keycloak authentication, local user & role administration, media management for property and room photos, tax calculation, fiscal invoicing & guest-registration providers, an **integration registry** for adapters and providers, door-lock credential hooks, revenue management — and **12 built-in AI agents** that orchestrate revenue strategy, optimize pricing, predict cancellations, detect audit anomalies, prioritize receivables collections, forecast group pickup, schedule housekeeping, automate guest communications, and draft review responses. External AI assistants book through the **Connect API**. Dashboard locales include English, German, and Portuguese (BR). All open source under Apache 2.0. What makes HAIP different is that **AI agents are built into the architecture from day one** — not as a bolt-on, but as first-class citizens with their own lifecycle, decision logging, and per-property calibration (deterministic engines that learn each hotel's own rates from its history — not LLMs pretending to be agents). HAIP is the sister project to [OTAIP](https://github.com/telivity-otaip/otaip) (Open Travel AI Platform). Together they form **Telivity's open-source travel infrastructure**. OTAIP agents connect to HAIP via the Connect API — the PMS works without AI, but the AI makes it extraordinary. @@ -58,7 +58,6 @@ graph TB subgraph Clients Dashboard["React Dashboard
(Vite + TanStack Query)"] BookingWidget["Booking Widget
(Direct Booking Engine)"] - ChatGPT["ChatGPT Gateway
(Connect GPT Actions)"] OTAIP["OTAIP Agents
(Hotel Search · Rate Compare · Booking)"] ThirdParty["Third-Party Systems
(Webhooks · Adapters)"] end @@ -121,7 +120,6 @@ graph TB Dashboard -->|HTTP + WebSocket| REST Dashboard -->|Real-time| WS BookingWidget -->|HTTP| BookingEngine - ChatGPT -->|REST| Connect OTAIP -->|REST| Connect ThirdParty -->|REST| REST ThirdParty -->|REST| Integrations @@ -464,13 +462,6 @@ Operator notes for activating existing adapters, metasearch landings on the dire - HMAC-signed deliveries with retries; permanent failures raise a critical staff notification - See **[`docs/webhooks.md`](./docs/webhooks.md)** for the integration guide (signature verification, payload conventions, fiscal documents, regional compliance examples) -### ChatGPT Gateway (Connect GPT) -- A standalone, deployable **gateway that exposes HAIP hotel search & booking as a ChatGPT Custom GPT Action** (`tools/haip-connect-gpt`) — guests search availability and create/modify/cancel reservations by chatting -- A thin, typed client over HAIP's existing **Connect API** (`/api/v1/connect/*`) — no hotel logic is reimplemented; it builds a ChatGPT-importable **OpenAPI 3.1** spec for 6 operations (`searchHotels`, `getProperty`, `create`/`get`/`modify`/`cancelReservation`) -- **Secure by design** — the gateway injects HAIP's API key server-side (the GPT never sees it), and response guards ensure **only selling prices** reach the model (net/wholesale/cost stripped) -- **PII-scrubbed tool-call logging** for training, with an optional Supabase/Postgres sink -- Host-agnostic — ships as a **Vercel** serverless function, a plain Node server, or a Docker container - ### Admin Dashboard - React SPA including Dashboard, Front Desk, Reservations, Guests, Rooms, Housekeeping, Folios, Groups, **Commercial**, Cashier, House Accounts, Accounting, Tax, Rate Plans, Night Audit, Reports, Channel Manager, Revenue Management, Communications, Reviews, Settings - Revenue Management page: KPI cards, pending AI recommendations with approve/reject, agent performance metrics, per-agent configuration @@ -752,8 +743,7 @@ haip/ │ │ └── src/schema/ # Table files (property, room, guest, agent, etc.) │ ├── shared/ # Shared types, enums, webhook events │ └── booking-requests/ # Optional request-first booking (flag-gated) -├── tools/ -│ └── haip-connect-gpt/ # ChatGPT Custom GPT gateway over the Connect API (Vercel) +├── tools/ # Repo-control and operator tooling ├── docker-compose.yml # PostgreSQL + Redis + Keycloak + API ├── CLAUDE.md # AI agent constitution └── .env.example # Environment template @@ -1096,9 +1086,7 @@ OTAIP's `packages/connect` will have a HAIP adapter (like `AmadeusAdapter`, `Duf OTAIP Agent → ConnectAdapter (HAIP) → Connect API → HAIP PMS → PostgreSQL ``` -The PMS is the product. OTAIP agents are the intelligence on top. - -> **ChatGPT too** — `tools/haip-connect-gpt` wraps this same Connect API as a ChatGPT Custom GPT Action (OpenAPI 3.1, server-side key injection, selling-price-only responses), so a guest can search and book straight from ChatGPT. See [`tools/haip-connect-gpt/README.md`](./tools/haip-connect-gpt/README.md). +The PMS is the product. OTAIP agents are the intelligence on top. External platforms (assistants, aggregators) use the same Connect API with a scoped API key — see [`docs/connect-external-booking.md`](./docs/connect-external-booking.md). --- diff --git a/apps/api/src/modules/connect/connect-booking.service.ts b/apps/api/src/modules/connect/connect-booking.service.ts index 1887b66b..d41beec2 100644 --- a/apps/api/src/modules/connect/connect-booking.service.ts +++ b/apps/api/src/modules/connect/connect-booking.service.ts @@ -146,7 +146,9 @@ export class ConnectBookingService { // the confirmation number is itself a bearer credential for the booking. const confirmationNumber = `HAIP-${generateConfirmationToken()}`; - const channelCode = dto.agentId ?? (hosted ? 'LIGARE' : 'otaip'); + // Prefer explicit agentId from the client (external gateways send their own + // channel codes). Neutral default for hosted checkout when omitted. + const channelCode = dto.agentId ?? (hosted ? 'connect' : 'otaip'); const externalRef = dto.externalReference ?? (dto.clientPlatform ? `${dto.clientPlatform}:${confirmationNumber}` : undefined); diff --git a/apps/api/src/modules/connect/connect.controller.ts b/apps/api/src/modules/connect/connect.controller.ts index e14abbbb..c38e2cd7 100644 --- a/apps/api/src/modules/connect/connect.controller.ts +++ b/apps/api/src/modules/connect/connect.controller.ts @@ -128,7 +128,7 @@ export class ConnectController { } @Get('bookings/:confirmationNumber/payment-status') - @ApiOperation({ summary: 'Payment status for hosted Connect / Ligare bookings (PSP-reconciled)' }) + @ApiOperation({ summary: 'Payment status for hosted Connect bookings (PSP-reconciled)' }) async paymentStatus(@Param('confirmationNumber') confirmationNumber: string) { return this.paymentService.getPaymentStatus(confirmationNumber); } diff --git a/apps/api/src/modules/connect/dto/agent-book.dto.ts b/apps/api/src/modules/connect/dto/agent-book.dto.ts index ea9882b1..14332837 100644 --- a/apps/api/src/modules/connect/dto/agent-book.dto.ts +++ b/apps/api/src/modules/connect/dto/agent-book.dto.ts @@ -97,14 +97,18 @@ export class AgentBookDto { @IsOptional() idempotencyKey?: string; - /** chatgpt | claude | gemini — stored for attribution UI */ - @ApiPropertyOptional({ enum: ['chatgpt', 'claude', 'gemini', 'ligare'] }) + /** Free-text client/platform tag for attribution (no fixed product enum). */ + @ApiPropertyOptional({ + description: 'Client platform identifier (free-text; e.g. assistant or integrator name)', + }) @IsOptional() - @IsIn(['chatgpt', 'claude', 'gemini', 'ligare']) - clientPlatform?: 'chatgpt' | 'claude' | 'gemini' | 'ligare'; + @IsString() + clientPlatform?: string; // Agent metadata - @ApiPropertyOptional({ description: 'Agent / channel id (use LIGARE for Ligare Hotels)' }) + @ApiPropertyOptional({ + description: 'Agent / channel id written to booking.channelCode when provided', + }) @IsOptional() @IsString() agentId?: string; diff --git a/docs/connect-external-booking.md b/docs/connect-external-booking.md new file mode 100644 index 00000000..b6831597 --- /dev/null +++ b/docs/connect-external-booking.md @@ -0,0 +1,65 @@ +# Connect API — external booking platforms + +HAIP’s Connect module lets external platforms (AI assistants, aggregators, partner apps) search and book inventory with a **scoped API key**. This document covers the booking surface only. Pricing and commercial terms for any particular distribution product are out of scope here. + +## Auth + +Send the platform key on every request: + +```http +x-api-key: +``` + +Keys are validated by `ApiKeyGuard` and scoped by `ConnectScopeGuard`. Do not embed property admin JWTs in client platforms. + +Base path: `/api/v1/connect`. + +## Book + +```http +POST /api/v1/connect/book +Content-Type: application/json +x-api-key: +``` + +Body (selected fields): + +| Field | Notes | +|-------|--------| +| `propertyId`, `roomTypeId`, `ratePlanId` | UUIDs | +| `checkIn`, `checkOut` | ISO dates | +| `guestFirstName`, `guestLastName` | Required | +| `guestEmail` | Required for `hosted_checkout` | +| `adults`, `children` | Occupancy | +| `paymentMethod` | `pay_at_property` \| `hosted_checkout` (preferred for assistants); `prepaid` / `virtual_card` accepted for compatibility | +| `idempotencyKey` | Required for `hosted_checkout` | +| `clientPlatform` | Optional free-text attribution tag | +| `agentId` | Optional channel code written to the booking when provided | +| `externalReference` | Optional partner reference | + +Hosted checkout returns a payment session / redirect URL; complete payment via the payment-status / confirm-payment (or PSP webhook) flow before the stay is fully confirmed. + +## Verify + +```http +GET /api/v1/connect/bookings/:confirmationNumber/verify +x-api-key: +``` + +Returns booking status and guest-safe details for the confirmation number. Possession of the confirmation number plus a valid API key is the access model. + +## Cancel + +```http +DELETE /api/v1/connect/bookings/:confirmationNumber +Content-Type: application/json +x-api-key: +``` + +Optional JSON body may include a `reason`. Penalties follow the property’s cancellation policy. + +## Related + +- Modify: `PATCH /api/v1/connect/bookings/:confirmationNumber` +- Hosted payment helpers: `…/payment-status`, `…/confirm-payment`, `…/mock-pay` (non-production), `…/payment-return` +- OpenAPI: served by the Nest API Swagger UI at `/docs` diff --git a/packages/database/src/migrations/0029_connect_payment_sessions.sql b/packages/database/src/migrations/0029_connect_payment_sessions.sql index bb8f57c8..0bfe5954 100644 --- a/packages/database/src/migrations/0029_connect_payment_sessions.sql +++ b/packages/database/src/migrations/0029_connect_payment_sessions.sql @@ -1,4 +1,4 @@ --- Connect LLM / agent hosted payment sessions (Ligare Hotels). +-- Connect LLM / agent hosted payment sessions. -- Tracks pending → paid/failed/expired with idempotency and PSP event dedupe. CREATE TABLE IF NOT EXISTS connect_payment_sessions ( diff --git a/packages/database/src/schema/connect.ts b/packages/database/src/schema/connect.ts index a3d9c851..9df17fa1 100644 --- a/packages/database/src/schema/connect.ts +++ b/packages/database/src/schema/connect.ts @@ -100,7 +100,7 @@ export const connectCredentials = pgTable('connect_credentials', { revokedAt: timestamp('revoked_at', { withTimezone: true }), }); -/** Hosted payment session for Connect / Ligare LLM bookings. */ +/** Hosted payment session for Connect agent / LLM bookings. */ export type ConnectPaymentMode = 'charge' | 'authorize' | 'guarantee'; export type ConnectPaymentSessionStatus = | 'pending' diff --git a/tools/haip-connect-gpt/.env.example b/tools/haip-connect-gpt/.env.example deleted file mode 100644 index 8364d1db..00000000 --- a/tools/haip-connect-gpt/.env.example +++ /dev/null @@ -1,25 +0,0 @@ -# --- Upstream HAIP Connect API --- -HAIP_API_BASE_URL=http://localhost:3000 -HAIP_CONNECT_API_KEY=dev-connect-key - -# --- This gateway (reference OSS adapter) --- -PORT=8080 -PUBLIC_BASE_URL=http://localhost:8080 - -# REQUIRED when exposing publicly — callers must present this. -GATEWAY_API_KEY= - -# Optional attribution defaults for Connect bookings -# HAIP_AGENT_ID=HAIP_AGENT - -# Optional static allow-list (comma-separated property UUIDs) -# ALLOWED_PROPERTY_IDS=00000000-0000-4000-a000-000000000001 -# Local demos only: -# ALLOW_ALL_PROPERTIES=true - -# Optional live allow-list API (service token) -# AI_DISTRIBUTION_BASE_URL= -# AI_DISTRIBUTION_SERVICE_TOKEN= - -# --- Tool-call logging --- -TOOL_LOG_DATABASE_URL= diff --git a/tools/haip-connect-gpt/.gitignore b/tools/haip-connect-gpt/.gitignore deleted file mode 100644 index e768a87f..00000000 --- a/tools/haip-connect-gpt/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -.env -.env.local -*.log -.vercel diff --git a/tools/haip-connect-gpt/CHATGPT-GPT.md b/tools/haip-connect-gpt/CHATGPT-GPT.md deleted file mode 100644 index c182d968..00000000 --- a/tools/haip-connect-gpt/CHATGPT-GPT.md +++ /dev/null @@ -1,7 +0,0 @@ -# HAIP Connect — reference agent gateway - -Open-source adapter that exposes HAIP Connect (`/api/v1/connect/*`) to ChatGPT Actions and MCP clients. - -Commercial AI distribution (paid enrollment, multi-PMS connectors, commission billing) is a **separate product** and is not documented in this public repository. - -See [README.md](./README.md) for local setup. diff --git a/tools/haip-connect-gpt/Dockerfile b/tools/haip-connect-gpt/Dockerfile deleted file mode 100644 index 06e0f198..00000000 --- a/tools/haip-connect-gpt/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Standalone gateway image (this package is outside the pnpm workspace). -FROM node:20-alpine AS build -WORKDIR /app -COPY package.json ./ -RUN npm install -COPY tsconfig.json ./ -COPY src ./src -RUN npm run build - -FROM node:20-alpine AS runtime -WORKDIR /app -ENV NODE_ENV=production -COPY package.json ./ -RUN npm install --omit=dev -COPY --from=build /app/dist ./dist -EXPOSE 8080 -CMD ["node", "dist/server.js"] diff --git a/tools/haip-connect-gpt/README.md b/tools/haip-connect-gpt/README.md deleted file mode 100644 index 28be063c..00000000 --- a/tools/haip-connect-gpt/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# HAIP Connect GPT gateway - -A thin, standalone **reference** gateway that exposes HAIP hotel search and booking as a -ChatGPT Custom GPT Action (and MCP). Wrap the Connect API, expose tools via OpenAPI, -host a tiny backend, and optionally log tool calls for debugging. - -HAIP's "domain engine" is its already-built **Connect API** (`/api/v1/connect/*`). This -gateway is a typed HTTP client over it — it does **not** reimplement hotel logic. - -``` -ChatGPT ──HTTPS──▶ this gateway ──x-api-key──▶ HAIP Connect API (/api/v1/connect/*) - │ - └──▶ Supabase (haip_tool_calls) # PII-scrubbed tool-call log -``` - -## Why a separate service - -- The gateway holds HAIP's `x-api-key` and injects it server-side — **the GPT never sees it**. -- The public AI surface (6 hotel operations) stays decoupled from HAIP's internal API. -- Responses are guarded so **only selling prices** ever reach the GPT (no net/wholesale/cost). - -## Pieces - -| File | Role | -| --- | --- | -| `src/haip-connect-adapter.ts` | Uniform hotel methods → HTTP calls to `/api/v1/connect/*` | -| `src/openapi.ts` | Builds the ChatGPT-importable OpenAPI 3.1 spec | -| `src/app.ts` / `src/server.ts` | Fastify backend: actions + `/openapi.json` + `/health` + `/privacy` | -| `src/events.ts` | Logs each tool call to Postgres (`haip_tool_calls` in the haip-demo Supabase project) | -| `src/scrub.ts` | PII redaction (for logs) + net-rate stripping (for responses) | -| `src/pages.ts` | Inlined landing + privacy HTML (no disk reads — portable to serverless) | -| `api/index.ts` | Vercel serverless entrypoint (wraps the same Fastify app) | - -## Endpoints - -| Method | Path | Tool | -| --- | --- | --- | -| POST | `/hotels/search` | `searchHotels` | -| GET | `/hotels/{propertyId}` | `getProperty` | -| POST | `/reservations` | `createReservation` | -| GET | `/reservations/{confirmationNumber}` | `getReservation` | -| PATCH | `/reservations/{confirmationNumber}` | `modifyReservation` | -| DELETE | `/reservations/{confirmationNumber}` | `cancelReservation` | - -Plus `GET /openapi.json`, `GET /health`, `GET /privacy`, `GET /`. - -## Run locally - -```bash -cd tools/haip-connect-gpt -cp .env.example .env # fill in HAIP_CONNECT_API_KEY and (optionally) Supabase keys -npm install -npm run dev # tsx watch -``` - -Point `HAIP_API_BASE_URL` at a running HAIP API (e.g. `http://localhost:3000` after -`pnpm --filter @telivityhaip/api dev` and seeding). Then: - -```bash -curl localhost:8080/health -curl localhost:8080/openapi.json | head -curl -X POST localhost:8080/hotels/search \ - -H 'content-type: application/json' \ - -d '{"city":"New York","checkIn":"2026-06-01","checkOut":"2026-06-03","adults":2}' -``` - -## Test - -```bash -npm test # vitest: scrub redaction + net-rate guard, adapter routing/errors -npm run build # tsc typecheck + emit -``` - -## Deploy (Vercel) - -This package deploys to Vercel as a single serverless function. `vercel.json` runs the -build and rewrites all routes to `api/index.ts`, which forwards into the same Fastify app. -Set these environment variables on the Vercel project: - -- `HAIP_API_BASE_URL` — your public HAIP API URL -- `HAIP_CONNECT_API_KEY` — matches HAIP's `CONNECT_API_KEY` -- `PUBLIC_BASE_URL` — optional; the production domain for the OpenAPI server URL - (auto-derived from the Vercel deployment domain if unset) -- `TOOL_LOG_DATABASE_URL` — haip-demo Postgres connection string (logging; optional) - -> The gateway can also run as a plain long-running Node server (`npm run start`) or in a -> container (`Dockerfile`) for non-serverless hosts — the core code is host-agnostic. - -## Publish the GPT - -See [`CHATGPT-GPT.md`](./CHATGPT-GPT.md) for the GPT name, description, instructions, and -the import steps (import the Action from `PUBLIC_BASE_URL/openapi.json`, set the privacy -URL to `PUBLIC_BASE_URL/privacy`). diff --git a/tools/haip-connect-gpt/api/index.ts b/tools/haip-connect-gpt/api/index.ts deleted file mode 100644 index f5ac209f..00000000 --- a/tools/haip-connect-gpt/api/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Vercel serverless entrypoint. - * - * `vercel.json` rewrites every path to this single function, which forwards the - * request into the same Fastify app used for local/Node hosting. The business - * logic (adapter, OpenAPI, scrub, logging) is unchanged — this is only the boot - * shim for serverless. Imports come from ../dist (produced by `npm run build`), - * so Vercel bundles plain compiled JS. - */ - -import type { IncomingMessage, ServerResponse } from 'node:http'; -import { HaipConnectAdapter } from '../dist/haip-connect-adapter.js'; -import { buildApp } from '../dist/app.js'; - -// Serverless boots leniently: a config-less first deploy must still serve -// /openapi.json, /privacy, and /health so the ChatGPT Action can be wired up -// (Stage A). Action routes will simply return an upstream error until -// HAIP_API_BASE_URL points at a reachable HAIP API (Stage B). The long-running -// server (server.ts) stays strict and fails loudly on missing config. -const baseUrl = process.env['HAIP_API_BASE_URL'] ?? 'https://haip-not-configured.invalid'; -const apiKey = process.env['HAIP_CONNECT_API_KEY'] ?? 'unconfigured'; - -// Prefer an explicit PUBLIC_BASE_URL; otherwise fall back to the Vercel-provided -// production/deployment domain so the OpenAPI `servers[0].url` is correct. -const publicBaseUrl = - process.env['PUBLIC_BASE_URL'] ?? - (process.env['VERCEL_PROJECT_PRODUCTION_URL'] - ? `https://${process.env['VERCEL_PROJECT_PRODUCTION_URL']}` - : process.env['VERCEL_URL'] - ? `https://${process.env['VERCEL_URL']}` - : ''); - -const app = buildApp({ - adapter: new HaipConnectAdapter({ baseUrl, apiKey }), - publicBaseUrl, - gatewayApiKey: process.env['GATEWAY_API_KEY'], -}); -const ready = app.ready(); - -export default async function handler(req: IncomingMessage, res: ServerResponse): Promise { - await ready; - app.server.emit('request', req, res); -} diff --git a/tools/haip-connect-gpt/package-lock.json b/tools/haip-connect-gpt/package-lock.json deleted file mode 100644 index 0cc5f11a..00000000 --- a/tools/haip-connect-gpt/package-lock.json +++ /dev/null @@ -1,2646 +0,0 @@ -{ - "name": "@telivityhaip/haip-connect-gpt", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@telivityhaip/haip-connect-gpt", - "version": "0.0.1", - "dependencies": { - "@fastify/cors": "^10.0.1", - "fastify": "^5.1.0", - "postgres": "^3.4.9" - }, - "devDependencies": { - "@types/node": "^20.16.5", - "tsx": "^4.19.1", - "typescript": "^5.6.2", - "vitest": "^2.1.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", - "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", - "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0" - } - }, - "node_modules/@fastify/cors": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-10.1.0.tgz", - "integrity": "sha512-MZyBCBJtII60CU9Xme/iE4aEy8G7QpzGR8zkdXZkDFt7ElEMachbE61tfhAG/bvSaULlqlf0huMT12T7iqEmdQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "fastify-plugin": "^5.0.0", - "mnemonist": "0.40.0" - } - }, - "node_modules/@fastify/error": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", - "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", - "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "fast-json-stringify": "^6.0.0" - } - }, - "node_modules/@fastify/forwarded": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", - "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", - "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/@fastify/proxy-addr": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", - "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/forwarded": "^3.0.0", - "ipaddr.js": "^2.1.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", - "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", - "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", - "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", - "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", - "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", - "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", - "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", - "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", - "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", - "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", - "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", - "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", - "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", - "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", - "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", - "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", - "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", - "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", - "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", - "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", - "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", - "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", - "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", - "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", - "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.42.tgz", - "integrity": "sha512-5L7SUaFC1RyDraj2yRhyBzHTobyXHmohD100CChNtyPyleoq37Mqab5Gn8XEKI04dfN/oqPdpHk38MgcQWHbZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "license": "MIT" - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/avvio": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz", - "integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/error": "^4.0.0", - "fastq": "^1.17.1" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-json-stringify": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.4.0.tgz", - "integrity": "sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/merge-json-schemas": "^0.2.0", - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0", - "json-schema-ref-resolver": "^3.0.0", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastify": { - "version": "5.8.5", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.5.tgz", - "integrity": "sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/ajv-compiler": "^4.0.5", - "@fastify/error": "^4.0.0", - "@fastify/fast-json-stringify-compiler": "^5.0.0", - "@fastify/proxy-addr": "^5.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^9.0.0", - "fast-json-stringify": "^6.0.0", - "find-my-way": "^9.0.0", - "light-my-request": "^6.0.0", - "pino": "^9.14.0 || ^10.1.0", - "process-warning": "^5.0.0", - "rfdc": "^1.3.1", - "secure-json-parse": "^4.0.0", - "semver": "^7.6.0", - "toad-cache": "^3.7.0" - } - }, - "node_modules/fastify-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", - "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/find-my-way": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.6.0.tgz", - "integrity": "sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", - "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/json-schema-ref-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", - "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/light-my-request": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", - "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "dependencies": { - "cookie": "^1.0.1", - "process-warning": "^4.0.0", - "set-cookie-parser": "^2.6.0" - } - }, - "node_modules/light-my-request/node_modules/process-warning": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", - "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/mnemonist": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.0.tgz", - "integrity": "sha512-kdd8AFNig2AD5Rkih7EPCXhu/iMvwevQFX/uEiGhZyPZi7fHqOoF4V4kHLpCfysxXMgQ4B52kdPMCwARshKvEg==", - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "license": "MIT" - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pino": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", - "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", - "license": "MIT", - "dependencies": { - "@pinojs/redact": "^0.4.0", - "atomic-sleep": "^1.0.0", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^3.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^4.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", - "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", - "license": "MIT", - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", - "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", - "license": "MIT" - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postgres": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.9.tgz", - "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==", - "license": "Unlicense", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/porsager" - } - }, - "node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ret": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", - "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", - "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.61.1", - "@rollup/rollup-android-arm64": "4.61.1", - "@rollup/rollup-darwin-arm64": "4.61.1", - "@rollup/rollup-darwin-x64": "4.61.1", - "@rollup/rollup-freebsd-arm64": "4.61.1", - "@rollup/rollup-freebsd-x64": "4.61.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", - "@rollup/rollup-linux-arm-musleabihf": "4.61.1", - "@rollup/rollup-linux-arm64-gnu": "4.61.1", - "@rollup/rollup-linux-arm64-musl": "4.61.1", - "@rollup/rollup-linux-loong64-gnu": "4.61.1", - "@rollup/rollup-linux-loong64-musl": "4.61.1", - "@rollup/rollup-linux-ppc64-gnu": "4.61.1", - "@rollup/rollup-linux-ppc64-musl": "4.61.1", - "@rollup/rollup-linux-riscv64-gnu": "4.61.1", - "@rollup/rollup-linux-riscv64-musl": "4.61.1", - "@rollup/rollup-linux-s390x-gnu": "4.61.1", - "@rollup/rollup-linux-x64-gnu": "4.61.1", - "@rollup/rollup-linux-x64-musl": "4.61.1", - "@rollup/rollup-openbsd-x64": "4.61.1", - "@rollup/rollup-openharmony-arm64": "4.61.1", - "@rollup/rollup-win32-arm64-msvc": "4.61.1", - "@rollup/rollup-win32-ia32-msvc": "4.61.1", - "@rollup/rollup-win32-x64-gnu": "4.61.1", - "@rollup/rollup-win32-x64-msvc": "4.61.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-regex2": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz", - "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "ret": "~0.5.0" - }, - "bin": { - "safe-regex2": "bin/safe-regex2.js" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/secure-json-parse": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", - "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/semver": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.3.tgz", - "integrity": "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/sonic-boom": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", - "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/thread-stream": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", - "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", - "license": "MIT", - "dependencies": { - "real-require": "^1.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/thread-stream/node_modules/real-require": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", - "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/toad-cache": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.1.tgz", - "integrity": "sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - } - } -} diff --git a/tools/haip-connect-gpt/package.json b/tools/haip-connect-gpt/package.json deleted file mode 100644 index 3a4f6a60..00000000 --- a/tools/haip-connect-gpt/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "@telivityhaip/haip-connect-gpt", - "version": "0.0.1", - "private": true, - "description": "ChatGPT Custom GPT gateway for HAIP — proxies the HAIP Connect API, serves a ChatGPT-importable OpenAPI 3.1 spec, and logs every tool call (PII-scrubbed) to Supabase.", - "type": "module", - "engines": { - "node": ">=20.0.0" - }, - "scripts": { - "build": "tsc -p tsconfig.json", - "dev": "tsx watch src/server.ts", - "start": "node dist/server.js", - "typecheck": "tsc -p tsconfig.json --noEmit", - "test": "vitest run" - }, - "dependencies": { - "@fastify/cors": "^10.0.1", - "fastify": "^5.1.0", - "postgres": "^3.4.9" - }, - "devDependencies": { - "@types/node": "^20.16.5", - "tsx": "^4.19.1", - "typescript": "^5.6.2", - "vitest": "^2.1.1" - } -} diff --git a/tools/haip-connect-gpt/scripts/e2e-smoke.mjs b/tools/haip-connect-gpt/scripts/e2e-smoke.mjs deleted file mode 100644 index f66a1563..00000000 --- a/tools/haip-connect-gpt/scripts/e2e-smoke.mjs +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env node -/** - * E2E smoke for the HAIP Connect agent gateway. - * - * Env: - * GATEWAY_BASE_URL — public gateway URL - * GATEWAY_API_KEY — optional Bearer - * CITY — default Paris - * PROPERTY_ID — optional property UUID pin - * - * Flow: search → (optional) prepareBooking → confirmPayment path check → OpenAPI/MCP. - */ -const base = (process.env.GATEWAY_BASE_URL || '').replace(/\/+$/, ''); -const key = process.env.GATEWAY_API_KEY || ''; -const city = process.env.CITY || 'Paris'; -const propertyId = process.env.PROPERTY_ID || ''; - -if (!base) { - console.error('GATEWAY_BASE_URL required'); - process.exit(1); -} - -const headers = { - 'content-type': 'application/json', - ...(key ? { authorization: `Bearer ${key}` } : {}), -}; - -async function req(method, path, body) { - const res = await fetch(`${base}${path}`, { - method, - headers, - body: body === undefined ? undefined : JSON.stringify(body), - }); - const text = await res.text(); - let json; - try { - json = text ? JSON.parse(text) : null; - } catch { - json = text; - } - return { status: res.status, json }; -} - -const results = []; - -const health = await req('GET', '/health'); -results.push(['health', health.status === 200]); - -const openapi = await req('GET', '/openapi.json'); -const hasConfirm = - openapi.status === 200 && - openapi.json?.paths?.['/reservations/{confirmationNumber}/confirm-payment']; -results.push(['openapi_confirmPayment', !!hasConfirm]); -results.push([ - 'openapi_no_paymentToken', - !openapi.json?.paths?.['/reservations']?.post?.requestBody?.content?.['application/json']?.schema - ?.properties?.paymentToken, -]); - -const mcp = await req('POST', '/mcp', { - jsonrpc: '2.0', - id: 1, - method: 'tools/list', -}); -results.push(['mcp_tools_list', mcp.status === 200 && Array.isArray(mcp.json?.result?.tools)]); - -const tomorrow = new Date(); -tomorrow.setUTCDate(tomorrow.getUTCDate() + 14); -const checkout = new Date(tomorrow); -checkout.setUTCDate(checkout.getUTCDate() + 2); -const checkIn = tomorrow.toISOString().slice(0, 10); -const checkOut = checkout.toISOString().slice(0, 10); - -const search = await req('POST', '/hotels/search', { - city, - ...(propertyId ? { propertyId } : {}), - checkIn, - checkOut, - adults: 2, -}); -results.push(['search', search.status === 200]); -const searchResults = search.json?.results ?? []; -results.push(['search_results_array', Array.isArray(searchResults)]); - -if (propertyId) { - const detail = await req('GET', `/hotels/${propertyId}`); - results.push(['property_detail_or_404', detail.status === 200 || detail.status === 404]); -} - -console.log(JSON.stringify({ base, checkIn, checkOut, city, results, searchCount: searchResults.length }, null, 2)); -const failed = results.filter(([, ok]) => !ok); -process.exit(failed.length ? 1 : 0); diff --git a/tools/haip-connect-gpt/src/allow-list.test.ts b/tools/haip-connect-gpt/src/allow-list.test.ts deleted file mode 100644 index 86f3067b..00000000 --- a/tools/haip-connect-gpt/src/allow-list.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, it, expect, vi } from 'vitest'; -import { AllowList, staticAllowListSource } from './allow-list.js'; - -describe('AllowList', () => { - it('caches search allow-list for ttl then refreshes', async () => { - let calls = 0; - const source = { - listEnabled: vi.fn(async () => { - calls += 1; - return calls === 1 ? ['a'] : ['a', 'b']; - }), - isEnabled: vi.fn(async (id: string) => id === 'a'), - }; - const list = new AllowList(source, 50); - expect(await list.isEnabledCached('a')).toBe(true); - expect(await list.isEnabledCached('b')).toBe(false); - expect(source.listEnabled).toHaveBeenCalledTimes(1); - await new Promise((r) => setTimeout(r, 60)); - expect(await list.isEnabledCached('b')).toBe(true); - expect(source.listEnabled).toHaveBeenCalledTimes(2); - }); - - it('live check bypasses cache so disabled properties 404 immediately', async () => { - const enabled = new Set(['prop-a']); - const source = { - listEnabled: async () => [...enabled], - isEnabled: async (id: string) => enabled.has(id), - }; - const list = new AllowList(source, 60_000); - expect(await list.isEnabledCached('prop-a')).toBe(true); - enabled.delete('prop-a'); - // Cached search still sees it - expect(await list.isEnabledCached('prop-a')).toBe(true); - // Live book/detail does not - expect(await list.isPropertyEnabledLive('prop-a')).toBe(false); - }); - - it('static source filters search results', async () => { - const list = new AllowList(staticAllowListSource(['p1', 'p2'])); - expect(await list.filterPropertyIds(['p1', 'p3', 'p2'])).toEqual(['p1', 'p2']); - }); -}); diff --git a/tools/haip-connect-gpt/src/allow-list.ts b/tools/haip-connect-gpt/src/allow-list.ts deleted file mode 100644 index 44acdce0..00000000 --- a/tools/haip-connect-gpt/src/allow-list.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Per-property AI distribution allow-list. - * - * Search may use a ≤60s cache. Detail and prepareBooking MUST call - * isPropertyEnabledLive() so a hotel toggled OFF is immediately 404. - */ - -export interface AllowListSource { - /** Fetch the full set of enabled property IDs. */ - listEnabled(): Promise; - /** Live check for one property (no cache). */ - isEnabled(propertyId: string): Promise; -} - -export class AllowList { - private cachedIds: Set | null = null; - private cachedAt = 0; - private readonly ttlMs: number; - - constructor( - private readonly source: AllowListSource, - ttlMs = 60_000, - ) { - this.ttlMs = ttlMs; - } - - async getCachedEnabledIds(): Promise> { - const now = Date.now(); - if (this.cachedIds && now - this.cachedAt < this.ttlMs) { - return this.cachedIds; - } - const ids = await this.source.listEnabled(); - this.cachedIds = new Set(ids); - this.cachedAt = now; - return this.cachedIds; - } - - /** Search filter — may be up to ttlMs stale. */ - async filterPropertyIds(ids: string[]): Promise { - const enabled = await this.getCachedEnabledIds(); - return ids.filter((id) => enabled.has(id)); - } - - async isEnabledCached(propertyId: string): Promise { - const enabled = await this.getCachedEnabledIds(); - return enabled.has(propertyId); - } - - /** Detail / book — always live. */ - async isPropertyEnabledLive(propertyId: string): Promise { - return this.source.isEnabled(propertyId); - } - - /** Test helper */ - clearCache(): void { - this.cachedIds = null; - this.cachedAt = 0; - } -} - -/** Remote allow-list API (service token). */ -export function distributionAllowListSource(opts: { - baseUrl: string; - serviceToken: string; - fetchImpl?: typeof fetch; -}): AllowListSource { - const base = opts.baseUrl.replace(/\/+$/, ''); - const fetchFn = opts.fetchImpl ?? fetch; - const headers = { - 'x-service-token': opts.serviceToken, - accept: 'application/json', - }; - - return { - async listEnabled() { - const res = await fetchFn(`${base}/api/ai-distribution/allow-list`, { headers }); - if (!res.ok) { - throw new Error(`allow-list fetch failed: ${res.status}`); - } - const body = (await res.json()) as { propertyIds?: string[] }; - return body.propertyIds ?? []; - }, - async isEnabled(propertyId: string) { - const res = await fetchFn( - `${base}/api/ai-distribution/allow-list/${encodeURIComponent(propertyId)}`, - { headers }, - ); - if (res.status === 404) return false; - if (!res.ok) throw new Error(`allow-list property check failed: ${res.status}`); - const body = (await res.json()) as { enabled?: boolean }; - return body.enabled === true; - }, - }; -} - -/** Env / static allow-list for local demos (comma-separated UUIDs). */ -export function staticAllowListSource(propertyIds: string[]): AllowListSource { - const set = new Set(propertyIds.filter(Boolean)); - return { - async listEnabled() { - return [...set]; - }, - async isEnabled(propertyId: string) { - return set.has(propertyId); - }, - }; -} - -export function createAllowListFromEnv(env: NodeJS.ProcessEnv = process.env): AllowList | null { - const distributionUrl = env['AI_DISTRIBUTION_BASE_URL']; - const token = env['AI_DISTRIBUTION_SERVICE_TOKEN']; - if (distributionUrl && token) { - return new AllowList( - distributionAllowListSource({ baseUrl: distributionUrl, serviceToken: token }), - ); - } - const staticIds = (env['ALLOWED_PROPERTY_IDS'] ?? '') - .split(',') - .map((s) => s.trim()) - .filter(Boolean); - if (staticIds.length > 0) { - return new AllowList(staticAllowListSource(staticIds)); - } - // Fail-closed when no source configured: empty allow-list. - // Set ALLOW_ALL_PROPERTIES=true only for local Connect demos without an allow-list API. - if (env['ALLOW_ALL_PROPERTIES'] === 'true') { - return new AllowList({ - async listEnabled() { - return []; - }, - async isEnabled() { - return true; - }, - }); - } - return new AllowList(staticAllowListSource([])); -} diff --git a/tools/haip-connect-gpt/src/app.auth.test.ts b/tools/haip-connect-gpt/src/app.auth.test.ts deleted file mode 100644 index 2ce7512e..00000000 --- a/tools/haip-connect-gpt/src/app.auth.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { buildApp } from './app.js'; - -function stubAdapter() { - return { - searchHotels: async () => ({ results: [] }), - getProperty: async () => ({ id: 'p1' }), - createReservation: async () => ({ confirmationNumber: 'HAIP-X', paymentUrl: 'https://pay.test' }), - confirmPayment: async () => ({ confirmed: true, paymentStatus: 'paid' }), - getReservation: async () => ({ confirmationNumber: 'HAIP-X' }), - modifyReservation: async () => ({ ok: true }), - cancelReservation: async () => ({ ok: true }), - upstreamHealthy: async () => true, - } as any; -} - -const GATEWAY_KEY = 'gw_secret_key'; - -describe('gateway authentication', () => { - it('allows /health without a credential', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', gatewayApiKey: GATEWAY_KEY }); - const res = await app.inject({ method: 'GET', url: '/health' }); - expect(res.statusCode).toBe(200); - await app.close(); - }); - - it('allows /openapi.json without a credential', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', gatewayApiKey: GATEWAY_KEY }); - const res = await app.inject({ method: 'GET', url: '/openapi.json' }); - expect(res.statusCode).toBe(200); - await app.close(); - }); - - it('rejects an action route with no credential (401)', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', gatewayApiKey: GATEWAY_KEY }); - const res = await app.inject({ method: 'POST', url: '/hotels/search', payload: {} }); - expect(res.statusCode).toBe(401); - await app.close(); - }); - - it('rejects an action route with a wrong credential (401)', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', gatewayApiKey: GATEWAY_KEY }); - const res = await app.inject({ - method: 'POST', - url: '/hotels/search', - headers: { authorization: 'Bearer wrong' }, - payload: {}, - }); - expect(res.statusCode).toBe(401); - await app.close(); - }); - - it('allows an action route with the correct Bearer credential', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', gatewayApiKey: GATEWAY_KEY }); - const res = await app.inject({ - method: 'POST', - url: '/hotels/search', - headers: { authorization: `Bearer ${GATEWAY_KEY}` }, - payload: {}, - }); - expect(res.statusCode).toBe(200); - await app.close(); - }); - - it('is open when no key is configured (demo posture)', async () => { - const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x' }); - const res = await app.inject({ method: 'POST', url: '/hotels/search', payload: {} }); - expect(res.statusCode).toBe(200); - await app.close(); - }); -}); diff --git a/tools/haip-connect-gpt/src/app.ts b/tools/haip-connect-gpt/src/app.ts deleted file mode 100644 index a3b64bb7..00000000 --- a/tools/haip-connect-gpt/src/app.ts +++ /dev/null @@ -1,323 +0,0 @@ -/** - * Fastify app: reference HAIP Connect agent gateway (ChatGPT Actions + MCP). - * - * Allow-list: search may cache ≤60s; getProperty / prepareBooking always live. - * Cards never enter the schema — hosted paymentUrl + confirmPayment only. - */ - -import Fastify, { type FastifyInstance, type FastifyReply, type FastifyRequest } from 'fastify'; -import cors from '@fastify/cors'; -import { timingSafeEqual, randomUUID } from 'node:crypto'; -import { - HaipConnectAdapter, - UpstreamError, - type BookInput, - type ModifyInput, - type SearchInput, -} from './haip-connect-adapter.js'; -import { buildOpenApiSpec } from './openapi.js'; -import { logToolCall } from './events.js'; -import { redactForLog } from './scrub.js'; -import { indexHtml, privacyHtml } from './pages.js'; -import { AllowList, createAllowListFromEnv } from './allow-list.js'; -import { handleMcpRequest, listMcpTools } from './mcp.js'; - -export interface AppOptions { - adapter: HaipConnectAdapter; - publicBaseUrl: string; - gatewayApiKey?: string; - allowList?: AllowList; - defaultClientPlatform?: BookInput['clientPlatform']; -} - -const PUBLIC_PATHS = new Set(['/health', '/openapi.json', '/', '/privacy']); - -function timingSafeEqualStr(a: string, b: string): boolean { - const ab = Buffer.from(a); - const bb = Buffer.from(b); - if (ab.length !== bb.length) return false; - return timingSafeEqual(ab, bb); -} - -function extractCredential(req: FastifyRequest): string | null { - const auth = req.headers['authorization']; - if (typeof auth === 'string' && auth.startsWith('Bearer ')) return auth.slice(7).trim(); - const key = req.headers['x-api-key']; - if (typeof key === 'string') return key; - if (Array.isArray(key)) return key[0] ?? null; - return null; -} - -function extractIdempotencyKey(req: FastifyRequest): string | undefined { - const h = req.headers['idempotency-key']; - if (typeof h === 'string' && h.trim()) return h.trim(); - if (Array.isArray(h) && h[0]) return h[0].trim(); - const body = req.body as { idempotencyKey?: string } | undefined; - return body?.idempotencyKey?.trim() || undefined; -} - -function propertyIdFromUnknown(value: unknown): string | null { - if (!value || typeof value !== 'object') return null; - const o = value as Record; - if (typeof o['propertyId'] === 'string') return o['propertyId']; - if (typeof o['sourcePropertyId'] === 'string') return o['sourcePropertyId']; - return null; -} - -export function buildApp(opts: AppOptions): FastifyInstance { - const { adapter, publicBaseUrl } = opts; - const allowList = opts.allowList ?? createAllowListFromEnv(); - const app = Fastify({ logger: true }); - - app.register(cors, { origin: true }); - - app.addHook('onRequest', async (req, reply) => { - if (!opts.gatewayApiKey) return; - const path = (req.url.split('?')[0] ?? req.url).replace(/\/+$/, '') || '/'; - if (PUBLIC_PATHS.has(path)) return; - // MCP may authenticate via same gateway key on non-public methods - if (path === '/mcp' && req.method === 'GET') return; - const provided = extractCredential(req); - if (!provided || !timingSafeEqualStr(provided, opts.gatewayApiKey)) { - reply.code(401).send({ error: 'unauthorized', message: 'A valid gateway credential is required.' }); - } - }); - - app.get('/health', async () => ({ - status: 'ok', - service: 'haip-connect-agent-gateway', - upstreamHealthy: await adapter.upstreamHealthy(), - timestamp: new Date().toISOString(), - })); - - app.get('/openapi.json', async (_req, reply) => { - reply.type('application/json').send(buildOpenApiSpec(publicBaseUrl)); - }); - - app.get('/', async (_req, reply) => reply.type('text/html').send(indexHtml)); - app.get('/privacy', async (_req, reply) => reply.type('text/html').send(privacyHtml)); - - app.get('/mcp', async (_req, reply) => { - reply.send({ - name: 'haip-connect', - transport: 'streamable-http', - tools: listMcpTools().map((t) => t.name), - endpoint: '/mcp', - }); - }); - - app.post('/mcp', async (req, reply) => { - const result = await handleMcpRequest(req.body, { - searchHotels: (input) => searchHotelsFiltered(adapter, allowList, input), - getProperty: (id) => getPropertyGuarded(adapter, allowList, id), - prepareBooking: (input, idem) => - prepareBookingGuarded(adapter, allowList, input, idem, opts.defaultClientPlatform), - confirmPayment: (cn) => adapter.confirmPayment(cn), - getReservation: (cn) => adapter.getReservation(cn), - modifyReservation: (cn, body) => adapter.modifyReservation(cn, body), - cancelReservation: (cn, reason) => adapter.cancelReservation(cn, reason), - }); - reply.send(result); - }); - - app.post( - '/hotels/search', - action('searchHotels', (req) => - searchHotelsFiltered(adapter, allowList, req.body as SearchInput), - ), - ); - - app.get( - '/hotels/:propertyId', - action('getProperty', (req) => - getPropertyGuarded( - adapter, - allowList, - (req.params as { propertyId: string }).propertyId, - ), - ), - ); - - app.post( - '/reservations', - action( - 'prepareBooking', - (req) => - prepareBookingGuarded( - adapter, - allowList, - req.body as BookInput, - extractIdempotencyKey(req), - opts.defaultClientPlatform, - ), - 201, - ), - ); - - app.post( - '/reservations/:confirmationNumber/confirm-payment', - action('confirmPayment', (req) => - adapter.confirmPayment( - (req.params as { confirmationNumber: string }).confirmationNumber, - ), - ), - ); - - app.get( - '/reservations/:confirmationNumber', - action('getReservation', (req) => - adapter.getReservation((req.params as { confirmationNumber: string }).confirmationNumber), - ), - ); - - app.patch( - '/reservations/:confirmationNumber', - action('modifyReservation', (req) => - adapter.modifyReservation( - (req.params as { confirmationNumber: string }).confirmationNumber, - req.body as ModifyInput, - ), - ), - ); - - app.delete( - '/reservations/:confirmationNumber', - action('cancelReservation', (req) => - adapter.cancelReservation( - (req.params as { confirmationNumber: string }).confirmationNumber, - (req.body as { reason?: string } | undefined)?.reason, - ), - ), - ); - - return app; -} - -async function searchHotelsFiltered( - adapter: HaipConnectAdapter, - allowList: AllowList, - input: SearchInput, -): Promise { - if (input.propertyId) { - const ok = await allowList.isEnabledCached(input.propertyId); - if (!ok) { - return { searchId: randomUUID(), totalResults: 0, results: [] }; - } - } - const raw = (await adapter.searchHotels(input)) as { - results?: Array>; - totalResults?: number; - searchId?: string; - [k: string]: unknown; - }; - const results = Array.isArray(raw.results) ? raw.results : []; - const enabled = await allowList.getCachedEnabledIds(); - // If allow-all sentinel (isEnabled always true with empty list), skip filter when empty+allow-all - const filtered = - enabled.size === 0 && process.env['ALLOW_ALL_PROPERTIES'] === 'true' - ? results - : results.filter((r) => { - const id = String(r['sourcePropertyId'] ?? r['propertyId'] ?? ''); - return enabled.has(id); - }); - return { - ...raw, - results: filtered, - totalResults: filtered.length, - }; -} - -async function getPropertyGuarded( - adapter: HaipConnectAdapter, - allowList: AllowList, - propertyId: string, -): Promise { - const ok = await allowList.isPropertyEnabledLive(propertyId); - if (!ok) { - throw new UpstreamError(404, { error: 'not_found', message: 'Property is not on the allow-list' }); - } - return adapter.getProperty(propertyId); -} - -async function prepareBookingGuarded( - adapter: HaipConnectAdapter, - allowList: AllowList, - input: BookInput, - idempotencyKey: string | undefined, - defaultPlatform?: BookInput['clientPlatform'], -): Promise { - const ok = await allowList.isPropertyEnabledLive(input.propertyId); - if (!ok) { - throw new UpstreamError(404, { error: 'not_found', message: 'Property is not on the allow-list' }); - } - if (!input.guestEmail) { - throw new UpstreamError(400, { error: 'guest_email_required', message: 'guestEmail is required' }); - } - const key = idempotencyKey ?? input.idempotencyKey; - if (!key) { - throw new UpstreamError(400, { - error: 'idempotency_key_required', - message: 'Idempotency-Key header or idempotencyKey body field is required', - }); - } - return adapter.createReservation({ - ...input, - idempotencyKey: key, - paymentMethod: 'hosted_checkout', - clientPlatform: input.clientPlatform ?? defaultPlatform ?? 'chatgpt', - agentId: process.env['HAIP_AGENT_ID'] ?? 'HAIP_AGENT', - }); -} - -function action( - tool: string, - run: (req: FastifyRequest) => Promise, - successStatus = 200, -) { - return async (req: FastifyRequest, reply: FastifyReply): Promise => { - const start = Date.now(); - const sessionId = sessionFrom(req); - const requestPayload = { params: req.params, query: req.query, body: req.body }; - const propertyId = - propertyIdFromUnknown(req.params) ?? - propertyIdFromUnknown(req.body) ?? - null; - - try { - const result = await run(req); - await logToolCall({ - tool, - sessionId, - propertyId, - request: redactForLog(requestPayload), - response: redactForLog(result), - status: 'ok', - latencyMs: Date.now() - start, - }); - reply.status(successStatus).send(result); - } catch (err) { - const status = err instanceof UpstreamError ? err.status : 500; - const body = - err instanceof UpstreamError - ? (err.body ?? { error: 'upstream_error' }) - : { error: 'gateway_error', message: (err as Error).message }; - await logToolCall({ - tool, - sessionId, - propertyId, - request: redactForLog(requestPayload), - response: null, - status: 'error', - error: typeof body === 'string' ? body : JSON.stringify(body), - latencyMs: Date.now() - start, - }); - reply.status(status).send(body); - } - }; -} - -function sessionFrom(req: FastifyRequest): string | null { - const raw = req.headers['openai-ephemeral-user-id'] ?? req.headers['x-session-id']; - if (Array.isArray(raw)) return raw[0] ?? null; - return (raw as string | undefined) ?? null; -} diff --git a/tools/haip-connect-gpt/src/events.ts b/tools/haip-connect-gpt/src/events.ts deleted file mode 100644 index 092f5702..00000000 --- a/tools/haip-connect-gpt/src/events.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Tool-call logging to the haip-demo Supabase project (table: haip_tool_calls). - * - * Writes via a direct Postgres connection (postgres-js) using TOOL_LOG_DATABASE_URL, - * so the gateway needs no Supabase service-role key. Logging is best-effort: a - * failure here (or a missing URL) never breaks the action response the GPT is - * waiting on. - * - * Callers MUST pass already-scrubbed request/response payloads (see scrub.ts). - */ - -import postgres from 'postgres'; - -export interface ToolCallLog { - tool: string; - sessionId: string | null; - propertyId?: string | null; - request: unknown; - response: unknown; - status: 'ok' | 'error'; - error?: string | null; - latencyMs: number; -} - -let sql: ReturnType | null = null; - -const url = process.env['TOOL_LOG_DATABASE_URL']; - -if (url) { - // Small pool: logging is fire-and-forget and must never starve the action path. - sql = postgres(url, { max: 2, connect_timeout: 10 }); -} else { - // Surfaced once at startup so an operator notices logging is off in production. - console.warn('[events] TOOL_LOG_DATABASE_URL not set — tool-call logging is disabled.'); -} - -export function isLoggingEnabled(): boolean { - return sql !== null; -} - -export async function logToolCall(entry: ToolCallLog): Promise { - if (!sql) return; - try { - // property_id column is optional on older haip_tool_calls tables — stored in request JSON too. - await sql` - insert into haip_tool_calls (tool, session_id, request, response, status, error, latency_ms) - values ( - ${entry.tool}, - ${entry.sessionId}, - ${sql.json({ ...(entry.request as object), _propertyId: entry.propertyId ?? null } as never)}, - ${sql.json(entry.response as never)}, - ${entry.status}, - ${entry.error ?? null}, - ${entry.latencyMs} - ) - `; - } catch (err) { - console.warn('[events] failed to log tool call:', (err as Error).message); - } -} diff --git a/tools/haip-connect-gpt/src/haip-connect-adapter.test.ts b/tools/haip-connect-gpt/src/haip-connect-adapter.test.ts deleted file mode 100644 index 8462c879..00000000 --- a/tools/haip-connect-gpt/src/haip-connect-adapter.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { HaipConnectAdapter, UpstreamError } from './haip-connect-adapter.js'; - -const BASE = 'http://haip.test'; -const KEY = 'test-key'; - -function adapter() { - return new HaipConnectAdapter({ baseUrl: `${BASE}/`, apiKey: KEY }); -} - -function mockFetch(status: number, body: unknown) { - return vi.fn(async () => - new Response(typeof body === 'string' ? body : JSON.stringify(body), { - status, - headers: { 'content-type': 'application/json' }, - }), - ); -} - -let originalFetch: typeof globalThis.fetch; -beforeEach(() => { - originalFetch = globalThis.fetch; -}); -afterEach(() => { - globalThis.fetch = originalFetch; - vi.restoreAllMocks(); -}); - -describe('HaipConnectAdapter routing', () => { - it('searchHotels POSTs to /api/v1/connect/search with x-api-key and JSON body', async () => { - const f = mockFetch(200, { results: [] }); - globalThis.fetch = f as unknown as typeof fetch; - - await adapter().searchHotels({ city: 'NYC', checkIn: '2026-06-01', checkOut: '2026-06-03' }); - - expect(f).toHaveBeenCalledTimes(1); - const [url, init] = f.mock.calls[0] as [string, RequestInit]; - expect(url).toBe(`${BASE}/api/v1/connect/search`); // trailing slash on baseUrl trimmed - expect(init.method).toBe('POST'); - expect((init.headers as Record)['x-api-key']).toBe(KEY); - expect(JSON.parse(init.body as string)).toMatchObject({ city: 'NYC' }); - }); - - it('getReservation GETs the verify path with encoded confirmation number', async () => { - const f = mockFetch(200, { status: 'confirmed' }); - globalThis.fetch = f as unknown as typeof fetch; - - await adapter().getReservation('AB/12'); - - const [url, init] = f.mock.calls[0] as [string, RequestInit]; - expect(url).toBe(`${BASE}/api/v1/connect/bookings/AB%2F12/verify`); - expect(init.method).toBe('GET'); - }); - - it('cancelReservation DELETEs and forwards the reason', async () => { - const f = mockFetch(200, { cancelled: true }); - globalThis.fetch = f as unknown as typeof fetch; - - await adapter().cancelReservation('XYZ', 'guest request'); - - const [url, init] = f.mock.calls[0] as [string, RequestInit]; - expect(url).toBe(`${BASE}/api/v1/connect/bookings/XYZ`); - expect(init.method).toBe('DELETE'); - expect(JSON.parse(init.body as string)).toEqual({ reason: 'guest request' }); - }); -}); - -describe('HaipConnectAdapter behavior', () => { - it('strips net-rate fields from successful responses', async () => { - globalThis.fetch = mockFetch(200, { - results: [{ rates: [{ totalAmount: 200, netRate: 150 }] }], - }) as unknown as typeof fetch; - - const out = (await adapter().searchHotels({ - checkIn: '2026-06-01', - checkOut: '2026-06-03', - })) as { results: { rates: Record[] }[] }; - - expect(out.results[0].rates[0]['totalAmount']).toBe(200); - expect(out.results[0].rates[0]).not.toHaveProperty('netRate'); - }); - - it('throws UpstreamError carrying status + body on non-2xx', async () => { - globalThis.fetch = mockFetch(404, { message: 'not found' }) as unknown as typeof fetch; - - await expect(adapter().getReservation('NOPE')).rejects.toMatchObject({ - status: 404, - body: { message: 'not found' }, - }); - await expect(adapter().getReservation('NOPE')).rejects.toBeInstanceOf(UpstreamError); - }); - - it('maps transport failures to a 502 UpstreamError', async () => { - globalThis.fetch = vi.fn(async () => { - throw new Error('ECONNREFUSED'); - }) as unknown as typeof fetch; - - await expect( - adapter().searchHotels({ checkIn: '2026-06-01', checkOut: '2026-06-03' }), - ).rejects.toMatchObject({ status: 502 }); - }); -}); diff --git a/tools/haip-connect-gpt/src/haip-connect-adapter.ts b/tools/haip-connect-gpt/src/haip-connect-adapter.ts deleted file mode 100644 index c3e458e9..00000000 --- a/tools/haip-connect-gpt/src/haip-connect-adapter.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * HaipConnectAdapter — typed HTTP client over HAIP Connect, - * but the "domain engine" here is HAIP's already-built Connect API. Each method is - * a single typed HTTP call to `${baseUrl}/api/v1/connect/*`, with the gateway's - * `x-api-key` injected server-side (the GPT never sees it). - * - * Every response is passed through stripNetRate() before it leaves the adapter, so - * no net/wholesale/cost field can ever reach the caller (selling price only). - */ - -import { stripNetRate } from './scrub.js'; - -export interface AdapterConfig { - baseUrl: string; - apiKey: string; - /** Per-request timeout in ms. Defaults to 20s. */ - timeoutMs?: number; -} - -/** Thrown for any non-2xx upstream response (or transport failure). */ -export class UpstreamError extends Error { - constructor( - public readonly status: number, - public readonly body: unknown, - ) { - super(`HAIP Connect API responded ${status}`); - this.name = 'UpstreamError'; - } -} - -// --- Request shapes (mirror HAIP's Connect DTOs; no invented fields) --- - -export interface SearchInput { - city?: string; - latitude?: number; - longitude?: number; - radiusKm?: number; - propertyId?: string; - checkIn: string; - checkOut: string; - rooms?: number; - adults?: number; - children?: number; - rateType?: string; - amenities?: string[]; - accessibleOnly?: boolean; - limit?: number; - offset?: number; -} - -export interface BookInput { - propertyId: string; - roomTypeId: string; - ratePlanId: string; - checkIn: string; - checkOut: string; - guestFirstName: string; - guestLastName: string; - guestEmail?: string; - guestPhone?: string; - loyaltyNumber?: string; - adults: number; - children?: number; - specialRequests?: string; - paymentMethod?: 'pay_at_property' | 'hosted_checkout'; - /** Required for hosted_checkout — stable per guest intent. */ - idempotencyKey?: string; - clientPlatform?: 'chatgpt' | 'claude' | 'gemini' | 'other'; - agentId?: string; - externalReference?: string; -} - -export interface ModifyInput { - guestFirstName?: string; - guestLastName?: string; - specialRequests?: string; - adults?: number; - children?: number; - checkIn?: string; - checkOut?: string; - roomTypeId?: string; - ratePlanId?: string; -} - -export class HaipConnectAdapter { - private readonly baseUrl: string; - private readonly apiKey: string; - private readonly timeoutMs: number; - - constructor(config: AdapterConfig) { - this.baseUrl = config.baseUrl.replace(/\/+$/, ''); - this.apiKey = config.apiKey; - this.timeoutMs = config.timeoutMs ?? 20_000; - } - - private async call( - method: string, - path: string, - body?: unknown, - ): Promise { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), this.timeoutMs); - let res: Response; - try { - res = await fetch(`${this.baseUrl}${path}`, { - method, - headers: { - 'content-type': 'application/json', - 'x-api-key': this.apiKey, - }, - body: body === undefined ? undefined : JSON.stringify(body), - signal: controller.signal, - }); - } catch (err) { - throw new UpstreamError(502, { - error: 'upstream_unreachable', - message: (err as Error).message, - }); - } finally { - clearTimeout(timer); - } - - const text = await res.text(); - const payload: unknown = text ? safeJson(text) : null; - - if (!res.ok) { - throw new UpstreamError(res.status, payload); - } - return stripNetRate(payload); - } - - searchHotels(input: SearchInput): Promise { - return this.call('POST', '/api/v1/connect/search', input); - } - - getProperty(propertyId: string): Promise { - return this.call('GET', `/api/v1/connect/properties/${encodeURIComponent(propertyId)}`); - } - - createReservation(input: BookInput): Promise { - const body = { - ...input, - paymentMethod: input.paymentMethod ?? 'hosted_checkout', - agentId: input.agentId ?? 'HAIP_AGENT', - clientPlatform: input.clientPlatform ?? 'chatgpt', - }; - // Never forward card tokens from an LLM client. - delete (body as { paymentToken?: string }).paymentToken; - return this.call('POST', '/api/v1/connect/book', body); - } - - confirmPayment(confirmationNumber: string): Promise { - return this.call( - 'POST', - `/api/v1/connect/bookings/${encodeURIComponent(confirmationNumber)}/confirm-payment`, - ); - } - - getPaymentStatus(confirmationNumber: string): Promise { - return this.call( - 'GET', - `/api/v1/connect/bookings/${encodeURIComponent(confirmationNumber)}/payment-status`, - ); - } - - getReservation(confirmationNumber: string): Promise { - return this.call( - 'GET', - `/api/v1/connect/bookings/${encodeURIComponent(confirmationNumber)}/verify`, - ); - } - - modifyReservation(confirmationNumber: string, changes: ModifyInput): Promise { - return this.call( - 'PATCH', - `/api/v1/connect/bookings/${encodeURIComponent(confirmationNumber)}`, - changes, - ); - } - - cancelReservation(confirmationNumber: string, reason?: string): Promise { - return this.call( - 'DELETE', - `/api/v1/connect/bookings/${encodeURIComponent(confirmationNumber)}`, - { reason }, - ); - } - - /** Liveness probe against the upstream HAIP API. */ - async upstreamHealthy(): Promise { - try { - const res = await fetch(`${this.baseUrl}/api/v1/health`); - return res.ok; - } catch { - return false; - } - } -} - -function safeJson(text: string): unknown { - try { - return JSON.parse(text); - } catch { - return text; - } -} diff --git a/tools/haip-connect-gpt/src/mcp.ts b/tools/haip-connect-gpt/src/mcp.ts deleted file mode 100644 index 76b58aea..00000000 --- a/tools/haip-connect-gpt/src/mcp.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Minimal Streamable-HTTP-friendly MCP JSON-RPC surface for Claude / Gemini. - * Supports initialize, tools/list, tools/call. - */ - -import type { BookInput, ModifyInput, SearchInput } from './haip-connect-adapter.js'; - -export interface McpToolHandlers { - searchHotels: (input: SearchInput) => Promise; - getProperty: (propertyId: string) => Promise; - prepareBooking: (input: BookInput, idempotencyKey?: string) => Promise; - confirmPayment: (confirmationNumber: string) => Promise; - getReservation: (confirmationNumber: string) => Promise; - modifyReservation: (confirmationNumber: string, body: ModifyInput) => Promise; - cancelReservation: (confirmationNumber: string, reason?: string) => Promise; -} - -const TOOL_DEFS = [ - { - name: 'searchHotels', - description: 'Search allow-listed hotels with live availability and tax-inclusive rates.', - inputSchema: { - type: 'object', - required: ['checkIn', 'checkOut'], - properties: { - city: { type: 'string' }, - propertyId: { type: 'string' }, - checkIn: { type: 'string' }, - checkOut: { type: 'string' }, - adults: { type: 'number' }, - children: { type: 'number' }, - }, - }, - }, - { - name: 'getProperty', - description: 'Get property detail (404 if not on the allow-list).', - inputSchema: { - type: 'object', - required: ['propertyId'], - properties: { propertyId: { type: 'string' } }, - }, - }, - { - name: 'prepareBooking', - description: - 'Create a pending booking and return paymentUrl + paymentMode. Requires guestEmail and idempotencyKey. Never send card data.', - inputSchema: { - type: 'object', - required: [ - 'propertyId', - 'roomTypeId', - 'ratePlanId', - 'checkIn', - 'checkOut', - 'guestFirstName', - 'guestLastName', - 'guestEmail', - 'adults', - 'idempotencyKey', - ], - properties: { - propertyId: { type: 'string' }, - roomTypeId: { type: 'string' }, - ratePlanId: { type: 'string' }, - checkIn: { type: 'string' }, - checkOut: { type: 'string' }, - guestFirstName: { type: 'string' }, - guestLastName: { type: 'string' }, - guestEmail: { type: 'string' }, - adults: { type: 'number' }, - children: { type: 'number' }, - idempotencyKey: { type: 'string' }, - clientPlatform: { type: 'string', enum: ['chatgpt', 'claude', 'gemini', 'other'] }, - specialRequests: { type: 'string' }, - }, - }, - }, - { - name: 'confirmPayment', - description: - 'Reconcile payment with the hotel PSP and confirm the reservation. Call after the guest pays via paymentUrl. Idempotent.', - inputSchema: { - type: 'object', - required: ['confirmationNumber'], - properties: { confirmationNumber: { type: 'string' } }, - }, - }, - { - name: 'getReservation', - description: 'Look up a reservation by confirmation number.', - inputSchema: { - type: 'object', - required: ['confirmationNumber'], - properties: { confirmationNumber: { type: 'string' } }, - }, - }, - { - name: 'modifyReservation', - description: 'Modify an existing reservation (send only changed fields).', - inputSchema: { - type: 'object', - required: ['confirmationNumber'], - properties: { - confirmationNumber: { type: 'string' }, - guestFirstName: { type: 'string' }, - guestLastName: { type: 'string' }, - specialRequests: { type: 'string' }, - adults: { type: 'number' }, - children: { type: 'number' }, - checkIn: { type: 'string' }, - checkOut: { type: 'string' }, - roomTypeId: { type: 'string' }, - ratePlanId: { type: 'string' }, - }, - }, - }, - { - name: 'cancelReservation', - description: 'Cancel a reservation. Confirm any penalty with the guest first.', - inputSchema: { - type: 'object', - required: ['confirmationNumber'], - properties: { - confirmationNumber: { type: 'string' }, - reason: { type: 'string' }, - }, - }, - }, -] as const; - -export function listMcpTools() { - return TOOL_DEFS; -} - -export async function handleMcpRequest( - body: unknown, - handlers: McpToolHandlers, -): Promise { - const msg = body as { - jsonrpc?: string; - id?: string | number | null; - method?: string; - params?: Record; - }; - - const id = msg?.id ?? null; - - if (msg?.method === 'initialize') { - return { - jsonrpc: '2.0', - id, - result: { - protocolVersion: '2024-11-05', - capabilities: { tools: {} }, - serverInfo: { name: 'haip-connect', version: '1.0.0' }, - }, - }; - } - - if (msg?.method === 'tools/list' || msg?.method === 'list_tools') { - return { - jsonrpc: '2.0', - id, - result: { tools: TOOL_DEFS }, - }; - } - - if (msg?.method === 'tools/call' || msg?.method === 'call_tool') { - const name = String(msg.params?.['name'] ?? ''); - const args = (msg.params?.['arguments'] ?? {}) as Record; - try { - const result = await dispatchTool(name, args, handlers); - return { - jsonrpc: '2.0', - id, - result: { - content: [{ type: 'text', text: JSON.stringify(result) }], - structuredContent: result, - }, - }; - } catch (err) { - return { - jsonrpc: '2.0', - id, - error: { - code: -32000, - message: (err as Error).message, - }, - }; - } - } - - if (msg?.method === 'notifications/initialized' || msg?.method === 'ping') { - return { jsonrpc: '2.0', id, result: {} }; - } - - return { - jsonrpc: '2.0', - id, - error: { code: -32601, message: `Method not found: ${msg?.method}` }, - }; -} - -async function dispatchTool( - name: string, - args: Record, - handlers: McpToolHandlers, -): Promise { - switch (name) { - case 'searchHotels': - return handlers.searchHotels(args as unknown as SearchInput); - case 'getProperty': - return handlers.getProperty(String(args['propertyId'])); - case 'prepareBooking': { - const { idempotencyKey, ...rest } = args; - return handlers.prepareBooking( - rest as unknown as BookInput, - idempotencyKey ? String(idempotencyKey) : undefined, - ); - } - case 'confirmPayment': - return handlers.confirmPayment(String(args['confirmationNumber'])); - case 'getReservation': - return handlers.getReservation(String(args['confirmationNumber'])); - case 'modifyReservation': { - const { confirmationNumber, ...changes } = args; - return handlers.modifyReservation( - String(confirmationNumber), - changes as unknown as ModifyInput, - ); - } - case 'cancelReservation': - return handlers.cancelReservation( - String(args['confirmationNumber']), - args['reason'] ? String(args['reason']) : undefined, - ); - default: - throw new Error(`Unknown tool: ${name}`); - } -} diff --git a/tools/haip-connect-gpt/src/openapi.test.ts b/tools/haip-connect-gpt/src/openapi.test.ts deleted file mode 100644 index 2ddcb41d..00000000 --- a/tools/haip-connect-gpt/src/openapi.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { buildOpenApiSpec } from './openapi.js'; - -describe('OpenAPI schema', () => { - const spec = buildOpenApiSpec('https://gateway.example') as any; - - it('forbids card fields on prepareBooking', () => { - const props = spec.paths['/reservations'].post.requestBody.content['application/json'].schema.properties; - expect(props.paymentToken).toBeUndefined(); - expect(props.cardNumber).toBeUndefined(); - expect(props.paymentMethod.enum).toEqual(['hosted_checkout', 'pay_at_property']); - expect(props.idempotencyKey).toBeTruthy(); - expect(props.guestEmail).toBeTruthy(); - }); - - it('exposes confirmPayment and paymentUrl/paymentMode', () => { - expect(spec.paths['/reservations/{confirmationNumber}/confirm-payment'].post.operationId).toBe( - 'confirmPayment', - ); - const resProps = - spec.paths['/reservations'].post.responses['201'].content['application/json'].schema.properties; - expect(resProps.paymentUrl).toBeTruthy(); - expect(resProps.paymentMode.enum).toEqual(['charge', 'authorize', 'guarantee']); - }); - - it('uses HAIP Connect branding', () => { - expect(String(spec.info.title)).toMatch(/HAIP Connect/i); - }); -}); diff --git a/tools/haip-connect-gpt/src/openapi.ts b/tools/haip-connect-gpt/src/openapi.ts deleted file mode 100644 index 600d4041..00000000 --- a/tools/haip-connect-gpt/src/openapi.ts +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Builds the ChatGPT-importable OpenAPI 3.1 spec for the Telivity HAIP GPT. - * - * Hand-authored (not derived from NestJS Swagger) so the public AI surface stays - * small, hotel-shaped, and decoupled from HAIP's internal endpoints. Request - * schemas mirror HAIP's Connect DTOs exactly — no invented hotel fields. Response - * schemas describe selling-price fields only. - */ - -export function buildOpenApiSpec(publicBaseUrl: string): Record { - const server = publicBaseUrl.replace(/\/+$/, ''); - - const cancellationPolicy = { - type: 'object', - description: 'Cancellation terms attached to this rate / booking.', - properties: { - type: { type: 'string', enum: ['free', 'non_refundable', 'tiered'] }, - freeCancellationBefore: { - type: 'string', - format: 'date-time', - description: 'Cancel free of charge before this instant (ISO 8601).', - }, - penaltyType: { type: 'string', enum: ['percentage', 'flat', 'first_night'] }, - penaltyAmount: { type: 'number' }, - description: { type: 'string' }, - }, - }; - - const nightly = { - type: 'array', - description: 'Per-night selling-price breakdown.', - items: { - type: 'object', - properties: { - date: { type: 'string', format: 'date' }, - baseRate: { type: 'number', description: 'Per-night selling price.' }, - taxAmount: { type: 'number' }, - totalRate: { type: 'number' }, - }, - }, - }; - - const rate = { - type: 'object', - properties: { - ratePlanId: { type: 'string', format: 'uuid' }, - ratePlanName: { type: 'string' }, - ratePlanCode: { type: 'string' }, - rateType: { - type: 'string', - description: 'e.g. bar, derived, negotiated, package, promotional.', - }, - totalAmount: { type: 'number', description: 'Total selling price for the stay.' }, - currencyCode: { type: 'string', example: 'USD' }, - nightlyBreakdown: nightly, - cancellationPolicy, - minLos: { type: 'integer' }, - maxLos: { type: 'integer' }, - }, - }; - - const roomType = { - type: 'object', - properties: { - roomTypeId: { type: 'string', format: 'uuid' }, - roomTypeName: { type: 'string' }, - description: { type: 'string' }, - maxOccupancy: { type: 'integer' }, - bedType: { type: 'string' }, - isAccessible: { type: 'boolean' }, - amenities: { type: 'array', items: { type: 'string' } }, - available: { type: 'integer', description: 'Rooms available across the date range.' }, - rates: { type: 'array', items: rate }, - }, - }; - - return { - openapi: '3.1.0', - info: { - title: 'HAIP Connect — Agent booking API', - description: - 'Search opted-in hotels, prepare a booking with hosted payment (no card data), confirm payment, then look up / modify / cancel. Selling prices are tax-inclusive.', - version: '1.0.0', - contact: { name: 'Telivity', url: server }, - }, - servers: [{ url: server }], - paths: { - '/hotels/search': { - post: { - operationId: 'searchHotels', - summary: 'Search hotels with availability and rates', - description: - 'Find available properties for a stay. Provide a location (city, or latitude/longitude, or a specific propertyId) plus check-in/check-out dates and occupancy. Returns properties with room types, rate plans, nightly breakdown, and cancellation policy.', - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - required: ['checkIn', 'checkOut'], - properties: { - city: { type: 'string', description: 'City name, e.g. "New York".' }, - latitude: { type: 'number' }, - longitude: { type: 'number' }, - radiusKm: { type: 'number', description: 'Search radius around lat/long, in km.' }, - propertyId: { - type: 'string', - format: 'uuid', - description: 'Restrict the search to a single known property.', - }, - checkIn: { type: 'string', format: 'date', description: 'Arrival date (YYYY-MM-DD).' }, - checkOut: { type: 'string', format: 'date', description: 'Departure date (YYYY-MM-DD).' }, - rooms: { type: 'integer', minimum: 1, default: 1 }, - adults: { type: 'integer', minimum: 1, default: 2 }, - children: { type: 'integer', minimum: 0, default: 0 }, - rateType: { type: 'string', description: 'Filter by rate type, e.g. "bar".' }, - amenities: { type: 'array', items: { type: 'string' } }, - accessibleOnly: { type: 'boolean' }, - limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 }, - offset: { type: 'integer', minimum: 0, default: 0 }, - }, - }, - }, - }, - }, - responses: { - '200': { - description: 'Search results.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - searchId: { type: 'string' }, - totalResults: { type: 'integer' }, - results: { - type: 'array', - items: { - type: 'object', - properties: { - sourcePropertyId: { type: 'string', format: 'uuid' }, - propertyName: { type: 'string' }, - propertyCode: { type: 'string' }, - starRating: { type: 'number' }, - address: { type: 'object', additionalProperties: true }, - roomTypes: { type: 'array', items: roomType }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - '/hotels/{propertyId}': { - get: { - operationId: 'getProperty', - summary: 'Get detailed property content', - parameters: [ - { - name: 'propertyId', - in: 'path', - required: true, - schema: { type: 'string', format: 'uuid' }, - }, - ], - responses: { - '200': { - description: 'Property detail.', - content: { 'application/json': { schema: { type: 'object', additionalProperties: true } } }, - }, - }, - }, - }, - '/reservations': { - post: { - operationId: 'prepareBooking', - summary: 'Prepare a reservation (hosted payment)', - description: - 'Create a pending booking and return paymentUrl + paymentMode. Guest pays on the hotel PSP hosted page — never send card data. Then call confirmPayment. Requires guestEmail and idempotencyKey.', - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - required: [ - 'propertyId', - 'roomTypeId', - 'ratePlanId', - 'checkIn', - 'checkOut', - 'guestFirstName', - 'guestLastName', - 'guestEmail', - 'adults', - 'idempotencyKey', - ], - properties: { - propertyId: { type: 'string', format: 'uuid' }, - roomTypeId: { type: 'string', format: 'uuid' }, - ratePlanId: { type: 'string', format: 'uuid' }, - checkIn: { type: 'string', format: 'date' }, - checkOut: { type: 'string', format: 'date' }, - guestFirstName: { type: 'string' }, - guestLastName: { type: 'string' }, - guestEmail: { type: 'string', format: 'email' }, - guestPhone: { type: 'string' }, - loyaltyNumber: { type: 'string' }, - adults: { type: 'integer', minimum: 1 }, - children: { type: 'integer', minimum: 0, default: 0 }, - specialRequests: { type: 'string' }, - idempotencyKey: { - type: 'string', - description: 'Stable key per guest booking intent (also send Idempotency-Key header).', - }, - clientPlatform: { - type: 'string', - enum: ['chatgpt', 'claude', 'gemini', 'other'], - }, - paymentMethod: { - type: 'string', - enum: ['hosted_checkout', 'pay_at_property'], - default: 'hosted_checkout', - }, - }, - }, - }, - }, - }, - responses: { - '201': { - description: 'Booking confirmed.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - success: { type: 'boolean' }, - confirmationNumber: { type: 'string' }, - status: { type: 'string' }, - totalAmount: { type: 'number' }, - taxAmount: { type: 'number' }, - currencyCode: { type: 'string' }, - paymentUrl: { type: 'string' }, - paymentMode: { type: 'string', enum: ['charge', 'authorize', 'guarantee'] }, - paymentStatus: { type: 'string' }, - cancellationPolicy, - }, - }, - }, - }, - }, - }, - }, - }, - '/reservations/{confirmationNumber}/confirm-payment': { - post: { - operationId: 'confirmPayment', - summary: 'Confirm payment after hosted checkout', - description: - 'Reconcile with the hotel PSP and confirm the reservation. Idempotent — safe to poll. Never invent a confirmation; only succeed when this returns paid/confirmed.', - parameters: [confirmationParam()], - responses: { - '200': { - description: 'Payment status after reconcile.', - content: { 'application/json': { schema: { type: 'object', additionalProperties: true } } }, - }, - }, - }, - }, - '/reservations/{confirmationNumber}': { - get: { - operationId: 'getReservation', - summary: 'Look up a reservation by confirmation number', - parameters: [confirmationParam()], - responses: { - '200': { - description: 'Reservation status and details.', - content: { 'application/json': { schema: { type: 'object', additionalProperties: true } } }, - }, - }, - }, - patch: { - operationId: 'modifyReservation', - summary: 'Modify a reservation', - description: - 'Change guest details, occupancy, dates, room type, or rate plan. Send only the fields being changed. Date / room / rate changes re-price the stay; the response includes the cost difference in selling price.', - parameters: [confirmationParam()], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - guestFirstName: { type: 'string' }, - guestLastName: { type: 'string' }, - specialRequests: { type: 'string' }, - adults: { type: 'integer', minimum: 1 }, - children: { type: 'integer', minimum: 0 }, - checkIn: { type: 'string', format: 'date' }, - checkOut: { type: 'string', format: 'date' }, - roomTypeId: { type: 'string', format: 'uuid' }, - ratePlanId: { type: 'string', format: 'uuid' }, - }, - }, - }, - }, - }, - responses: { - '200': { - description: 'Modification result.', - content: { 'application/json': { schema: { type: 'object', additionalProperties: true } } }, - }, - }, - }, - delete: { - operationId: 'cancelReservation', - summary: 'Cancel a reservation', - description: - 'Cancel a booking. The response includes any cancellation penalty and refund amount (selling price). Confirm the penalty with the guest before cancelling.', - parameters: [confirmationParam()], - requestBody: { - required: false, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { reason: { type: 'string' } }, - }, - }, - }, - }, - responses: { - '200': { - description: 'Cancellation result.', - content: { 'application/json': { schema: { type: 'object', additionalProperties: true } } }, - }, - }, - }, - }, - }, - }; -} - -function confirmationParam(): Record { - return { - name: 'confirmationNumber', - in: 'path', - required: true, - description: 'The confirmation number returned by createReservation.', - schema: { type: 'string' }, - }; -} diff --git a/tools/haip-connect-gpt/src/pages.ts b/tools/haip-connect-gpt/src/pages.ts deleted file mode 100644 index 62f5f114..00000000 --- a/tools/haip-connect-gpt/src/pages.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Landing and privacy pages, inlined as strings so the app never reads from disk. - * This keeps the gateway portable across long-running (Node) and serverless - * (Vercel) hosting — no filesystem/bundling assumptions. - */ - -export const indexHtml = ` - - - - - HAIP Connect — Agent Gateway - - - -

HAIP Connect agent gateway

-

Reference ChatGPT Actions + MCP adapter for the open-source HAIP Connect API.

-

- This service exposes hotel search and booking to AI assistants. It proxies the - HAIP Connect API and returns guest-facing selling prices only. -

- -

Available operations: searchHotels, getProperty, - prepareBooking, confirmPayment, getReservation, - modifyReservation, cancelReservation.

-
© Telivity. HAIP is an open-source, API-first hotel PMS.
- -`; - -export const privacyHtml = ` - - - - - Privacy — HAIP Connect agent gateway - - - -

Privacy Policy — Telivity HAIP

-

This is the privacy policy for the Telivity HAIP ChatGPT gateway.

- -

What this service does

-

- Telivity HAIP is a gateway that lets a ChatGPT assistant search hotels and create, - look up, modify, or cancel reservations by calling the HAIP (Hotel AI Platform) - Connect API on your behalf. -

- -

Data we process

-

- To complete a booking, the assistant sends booking details to this gateway: stay - dates, occupancy, the selected property/room/rate, and the guest details required by - the hotel (name, and optionally email, phone, and loyalty number). These are - forwarded to the HAIP API solely to fulfil your request. -

- -

Logging

-

- We log each request to improve the service. Before logging, we redact guest personal - information (name, email, phone, loyalty number, special requests), payment tokens, - and booking confirmation numbers. Logged records contain non-identifying details such - as location, dates, occupancy, room/rate identifiers, status, and timing, associated - with a pseudonymous per-conversation session identifier. -

- -

Payments

-

- This gateway never stores raw card data. Payment is handled by the hotel platform via - tokenization. -

- -

Data sharing

-

- Guests pay on the hotel's hosted payment page — card data never passes through this gateway or the LLM. We do not sell your data. Booking details are shared only with the hotel platform - (HAIP) needed to fulfil your reservation. -

- -

Contact

-

For privacy questions, contact Telivity.

- -
© Telivity.
- -`; diff --git a/tools/haip-connect-gpt/src/scrub.test.ts b/tools/haip-connect-gpt/src/scrub.test.ts deleted file mode 100644 index 3e201b1a..00000000 --- a/tools/haip-connect-gpt/src/scrub.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { redactForLog, stripNetRate } from './scrub.js'; - -describe('redactForLog', () => { - it('redacts guest PII and booking credentials, keeps analytical fields', () => { - const input = { - body: { - city: 'New York', - checkIn: '2026-06-01', - adults: 2, - guestFirstName: 'John', - guestLastName: 'Smith', - guestEmail: 'john@example.com', - guestPhone: '+1-555-0100', - loyaltyNumber: 'GOLD-1', - specialRequests: 'high floor', - paymentToken: 'tok_123', - }, - params: { confirmationNumber: 'ABC123' }, - }; - - const out = redactForLog(input) as typeof input; - - // kept - expect(out.body.city).toBe('New York'); - expect(out.body.checkIn).toBe('2026-06-01'); - expect(out.body.adults).toBe(2); - // redacted - expect(out.body.guestFirstName).toBe('[redacted]'); - expect(out.body.guestLastName).toBe('[redacted]'); - expect(out.body.guestEmail).toBe('[redacted]'); - expect(out.body.guestPhone).toBe('[redacted]'); - expect(out.body.loyaltyNumber).toBe('[redacted]'); - expect(out.body.specialRequests).toBe('[redacted]'); - expect(out.body.paymentToken).toBe('[redacted]'); - expect(out.params.confirmationNumber).toBe('[redacted]'); - }); - - it('redacts PII nested inside arrays', () => { - const out = redactForLog({ guests: [{ guestName: 'Jane Doe', adults: 1 }] }) as { - guests: { guestName: string; adults: number }[]; - }; - expect(out.guests[0].guestName).toBe('[redacted]'); - expect(out.guests[0].adults).toBe(1); - }); - - it('does not mutate the input', () => { - const input = { guestEmail: 'a@b.com' }; - redactForLog(input); - expect(input.guestEmail).toBe('a@b.com'); - }); -}); - -describe('stripNetRate', () => { - it('removes net / wholesale / commission fields', () => { - const out = stripNetRate({ - totalAmount: 400, - netRate: 320, - wholesaleAmount: 300, - commission: 80, - markup: 25, - }) as Record; - - expect(out['totalAmount']).toBe(400); - expect(out).not.toHaveProperty('netRate'); - expect(out).not.toHaveProperty('wholesaleAmount'); - expect(out).not.toHaveProperty('commission'); - expect(out).not.toHaveProperty('markup'); - }); - - it('keeps legitimate selling-price fields whose names merely contain "cost"', () => { - const out = stripNetRate({ costDifference: 50, previousAmount: 350, newAmount: 400 }) as Record< - string, - unknown - >; - expect(out['costDifference']).toBe(50); - expect(out['previousAmount']).toBe(350); - expect(out['newAmount']).toBe(400); - }); - - it('strips net fields nested in results arrays', () => { - const out = stripNetRate({ - results: [{ rates: [{ totalAmount: 200, netAmount: 160 }] }], - }) as { results: { rates: Record[] }[] }; - expect(out.results[0].rates[0]['totalAmount']).toBe(200); - expect(out.results[0].rates[0]).not.toHaveProperty('netAmount'); - }); -}); diff --git a/tools/haip-connect-gpt/src/scrub.ts b/tools/haip-connect-gpt/src/scrub.ts deleted file mode 100644 index 507d1579..00000000 --- a/tools/haip-connect-gpt/src/scrub.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * PII scrubbing (for logging) and net-rate guarding (for responses sent to the GPT). - * - * Two independent transforms, both deep and non-mutating: - * - * - redactForLog() — replaces guest PII + bearer credentials with "[redacted]" - * before a tool call is written to Supabase. We keep the analytically useful - * fields (location, dates, occupancy, room/rate ids, rate types, result counts). - * - * - stripNetRate() — removes any wholesale/net/cost-basis fields from upstream - * responses before they reach ChatGPT. HAIP's Connect API already returns - * selling prices only, so this is defense-in-depth: it enforces the selling-price-only rule - * ("the GPT sees the selling price only") even if a future HAIP change starts - * returning a net field. - */ - -type Json = unknown; - -/** - * Keys (compared case-insensitively) whose values are guest PII or a booking - * credential. Redacted — not removed — so the shape of logged payloads is stable. - */ -const PII_KEYS = new Set([ - 'guestfirstname', - 'guestlastname', - 'guestname', - 'guestemail', - 'email', - 'guestphone', - 'phone', - 'loyaltynumber', - 'specialrequests', - 'paymenttoken', - // Bearer credentials — possession of these authorizes booking access. - 'confirmationnumber', - 'cancellationnumber', -]); - -/** - * Keys (compared case-insensitively) that would expose net/wholesale/cost basis. - * Exact-name matching is deliberate: a broad /cost/ regex would wrongly strip - * legitimate selling-price fields such as `costDifference` (the change in the - * guest-facing total on a modify). - */ -const NET_RATE_KEYS = new Set([ - 'net', - 'netrate', - 'net_rate', - 'netamount', - 'net_amount', - 'netprice', - 'costrate', - 'cost_rate', - 'costprice', - 'cost_price', - 'costbasis', - 'wholesale', - 'wholesalerate', - 'wholesaleamount', - 'commission', - 'commissionrate', - 'commissionamount', - 'markup', - 'margin', - 'partnermargin', - 'profit', -]); - -function transform(value: Json, keyMatches: (lowerKey: string) => 'redact' | 'drop' | 'keep'): Json { - if (Array.isArray(value)) { - return value.map((item) => transform(item, keyMatches)); - } - if (value && typeof value === 'object') { - const out: Record = {}; - for (const [key, val] of Object.entries(value as Record)) { - const decision = keyMatches(key.toLowerCase()); - if (decision === 'drop') continue; - if (decision === 'redact') { - out[key] = '[redacted]'; - continue; - } - out[key] = transform(val, keyMatches); - } - return out; - } - return value; -} - -/** Deep-redact guest PII and booking credentials for safe logging. */ -export function redactForLog(value: Json): Json { - return transform(value, (k) => (PII_KEYS.has(k) ? 'redact' : 'keep')); -} - -/** Deep-remove any net/wholesale/cost-basis fields before a response reaches the GPT. */ -export function stripNetRate(value: Json): Json { - return transform(value, (k) => (NET_RATE_KEYS.has(k) ? 'drop' : 'keep')); -} diff --git a/tools/haip-connect-gpt/src/server.ts b/tools/haip-connect-gpt/src/server.ts deleted file mode 100644 index a06dbaf8..00000000 --- a/tools/haip-connect-gpt/src/server.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Boot entrypoint. Reads config from the environment, wires the adapter, and - * starts the Fastify server. - */ - -import { HaipConnectAdapter } from './haip-connect-adapter.js'; -import { buildApp } from './app.js'; -import { isLoggingEnabled } from './events.js'; - -const baseUrl = process.env['HAIP_API_BASE_URL']; -const apiKey = process.env['HAIP_CONNECT_API_KEY']; - -if (!baseUrl) { - console.error('FATAL: HAIP_API_BASE_URL is required.'); - process.exit(1); -} -if (!apiKey) { - console.error('FATAL: HAIP_CONNECT_API_KEY is required.'); - process.exit(1); -} - -const port = Number(process.env['PORT'] ?? 8080); -const publicBaseUrl = process.env['PUBLIC_BASE_URL'] ?? `http://localhost:${port}`; - -const adapter = new HaipConnectAdapter({ baseUrl, apiKey }); -const app = buildApp({ - adapter, - publicBaseUrl, - gatewayApiKey: process.env['GATEWAY_API_KEY'], -}); - -app - .listen({ port, host: '0.0.0.0' }) - .then(() => { - app.log.info(`HAIP Connect GPT gateway on :${port}`); - app.log.info(` upstream HAIP : ${baseUrl}`); - app.log.info(` public URL : ${publicBaseUrl}`); - app.log.info(` OpenAPI : ${publicBaseUrl}/openapi.json`); - app.log.info(` tool logging : ${isLoggingEnabled() ? 'enabled' : 'disabled'}`); - }) - .catch((err) => { - app.log.error(err); - process.exit(1); - }); diff --git a/tools/haip-connect-gpt/tsconfig.json b/tools/haip-connect-gpt/tsconfig.json deleted file mode 100644 index 74ab5788..00000000 --- a/tools/haip-connect-gpt/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "lib": ["ES2022"], - "outDir": "dist", - "rootDir": "src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "declaration": true, - "sourceMap": true, - "resolveJsonModule": true - }, - "include": ["src/**/*.ts"], - "exclude": ["dist", "node_modules", "src/**/*.test.ts"] -} diff --git a/tools/haip-connect-gpt/vercel.json b/tools/haip-connect-gpt/vercel.json deleted file mode 100644 index 342c3ed2..00000000 --- a/tools/haip-connect-gpt/vercel.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://openapi.vercel.sh/vercel.json", - "buildCommand": "npm run build", - "rewrites": [{ "source": "/(.*)", "destination": "/api" }] -} diff --git a/tools/repo-control/config.haip.yaml b/tools/repo-control/config.haip.yaml index 0cfe0be3..88c3157c 100644 --- a/tools/repo-control/config.haip.yaml +++ b/tools/repo-control/config.haip.yaml @@ -305,8 +305,6 @@ capabilities: requiredPaths: - apps/api/src/modules/connect - packages/database/src/schema/connect.ts - optionalPaths: - - tools/haip-connect-gpt routeHints: - connect testPaths: