From 385b2b68868dffbc926866f332b8f274aab28c57 Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 07:32:27 -0500 Subject: [PATCH] gateway: serve the SPA bundle same-origin (#1444) New spa/serve-spa.ts: @fastify/static with wildcard:false plus a GET /* catch-all that returns index.html for non-backend paths and a JSON 404 for unknown /api, /mcp, /socket.io paths. WEB_DIST_DIR unset disables serving (dev uses the Vite dev server, which proxies to the gateway); set but invalid fails loud at boot. A wildcard route, not setNotFoundHandler: Nest installs its own not-found handler at init and Fastify allows only one. find-my-way matches most-specific-first, so declared routes win over the catch-all. Cache semantics stay the library default (max-age=0 + ETag revalidation); immutable hashed-asset caching is deferred to P6. gateway.Dockerfile builds the web bundle and ships it at /app/web-dist with WEB_DIST_DIR set. --- apps/gateway/package.json | 1 + apps/gateway/src/main.ts | 2 + apps/gateway/src/spa/serve-spa.ts | 75 +++++++++++++ docker/gateway.Dockerfile | 9 +- pnpm-lock.yaml | 169 +++++++++++++++++++----------- 5 files changed, 194 insertions(+), 62 deletions(-) create mode 100644 apps/gateway/src/spa/serve-spa.ts diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 8e5819f9..3965b889 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -28,6 +28,7 @@ "dependencies": { "@anthropic-ai/sdk": "^0.80.0", "@fastify/helmet": "^13.0.2", + "@fastify/static": "^8.3.0", "@mariozechner/pi-ai": "^0.65.0", "@mariozechner/pi-coding-agent": "^0.65.0", "@modelcontextprotocol/sdk": "^1.27.1", diff --git a/apps/gateway/src/main.ts b/apps/gateway/src/main.ts index cb066d18..34e496d2 100644 --- a/apps/gateway/src/main.ts +++ b/apps/gateway/src/main.ts @@ -12,6 +12,7 @@ import { AppModule } from './app.module.js'; import { mountAuthHandler } from './auth/auth.controller.js'; import { mountMcpHandler } from './mcp/mcp.controller.js'; import { McpService } from './mcp/mcp.service.js'; +import { mountSpaStatic } from './spa/serve-spa.js'; import { detectAndAssertTier, TierDetectionError } from '@mosaicstack/storage'; import { resolveGatewayConfigPath } from './env.js'; import { assertValidationPipeSeesDtoDecorators } from './validation-pipe-check.js'; @@ -68,6 +69,7 @@ async function bootstrap(): Promise { mountAuthHandler(app); mountMcpHandler(app, app.get(McpService)); + await mountSpaStatic(app); const port = Number(process.env['GATEWAY_PORT'] ?? 14242); await app.listen(port, '0.0.0.0'); diff --git a/apps/gateway/src/spa/serve-spa.ts b/apps/gateway/src/spa/serve-spa.ts new file mode 100644 index 00000000..bda3617f --- /dev/null +++ b/apps/gateway/src/spa/serve-spa.ts @@ -0,0 +1,75 @@ +import { existsSync } from 'node:fs'; +import path from 'node:path'; +import { Logger } from '@nestjs/common'; +import fastifyStatic from '@fastify/static'; +import type { NestFastifyApplication } from '@nestjs/platform-fastify'; + +/** Request paths that belong to the backend, never to the SPA fallback. */ +const BACKEND_PREFIXES = ['/api', '/mcp', '/socket.io'] as const; + +function isBackendPath(url: string): boolean { + return BACKEND_PREFIXES.some((prefix) => url === prefix || url.startsWith(`${prefix}/`)); +} + +/** + * Serve the built web SPA bundle (Phase P5 cutover, #1444). + * + * WEB_DIST_DIR unset: SPA serving is disabled — dev runs the Vite dev server, + * which proxies /api and /socket.io here. WEB_DIST_DIR set but not holding a + * built bundle: fail at boot, because a gateway configured to serve the UI + * silently serving 404s is an outage, not a degraded mode. + * + * Static files get exact routes (wildcard: false, so nothing shadows the API + * routes); every other GET/HEAD outside the backend prefixes falls back to + * index.html so client-side routes deep-link correctly. + */ +export async function mountSpaStatic(app: NestFastifyApplication): Promise { + const logger = new Logger('SpaStatic'); + const distDir = process.env['WEB_DIST_DIR']; + if (!distDir) { + logger.log('WEB_DIST_DIR not set; SPA serving disabled (dev mode uses the Vite dev server)'); + return; + } + + const root = path.resolve(distDir); + const indexFile = path.join(root, 'index.html'); + if (!existsSync(indexFile)) { + throw new Error(`WEB_DIST_DIR is '${distDir}' but '${indexFile}' does not exist`); + } + + // Default cache semantics: public, max-age=0 with ETag/Last-Modified, so + // every response revalidates (304 when unchanged). Always correct, including + // for index.html after a deploy; immutable caching for hashed /assets/ files + // is a P6 optimization. + await app.register( + fastifyStatic as never, + { + root, + wildcard: false, + index: false, + } as never, + ); + + // A wildcard route, not setNotFoundHandler: Nest installs its own not-found + // handler during init and Fastify allows only one. find-my-way matches + // most-specific-first, so every declared route (API, static files) wins over + // this catch-all; non-GET unmatched requests keep Fastify's stock 404. + const fastify = app.getHttpAdapter().getInstance(); + fastify.get('/*', (req, reply) => { + const url = req.raw.url ?? ''; + if (isBackendPath(url)) { + // An unknown backend path is an API 404, never the SPA page. + void reply.code(404).send({ + message: `Route ${req.raw.method ?? 'GET'}:${url} not found`, + error: 'Not Found', + statusCode: 404, + }); + return; + } + // sendFile is decorated by @fastify/static; its type augmentation targets + // a different fastify copy in the pnpm tree than the Nest adapter's. + (reply as unknown as { sendFile: (file: string) => unknown }).sendFile('index.html'); + }); + + logger.log(`Serving SPA bundle from ${root}`); +} diff --git a/docker/gateway.Dockerfile b/docker/gateway.Dockerfile index 6534f188..1dc4feef 100644 --- a/docker/gateway.Dockerfile +++ b/docker/gateway.Dockerfile @@ -8,14 +8,16 @@ WORKDIR /app # Copy workspace manifests first for layer-cached install COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ COPY apps/gateway/package.json ./apps/gateway/ +COPY apps/web/package.json ./apps/web/ COPY packages/ ./packages/ COPY plugins/ ./plugins/ # the root prepare script runs scripts/install-hooks.mjs on install COPY scripts/ ./scripts/ RUN pnpm install --frozen-lockfile COPY . . -# Build gateway and all of its workspace dependencies via turbo dependency graph -RUN pnpm turbo run build --filter @mosaicstack/gateway... +# Build gateway, the web SPA bundle it serves (#1444), and all of their +# workspace dependencies via the turbo dependency graph +RUN pnpm turbo run build --filter @mosaicstack/gateway... --filter @mosaicstack/web... # Produce a self-contained deploy artifact: flat node_modules, no pnpm symlinks # --legacy is required for pnpm v10 when inject-workspace-packages is not set RUN pnpm --filter @mosaicstack/gateway --prod deploy --legacy /deploy @@ -38,6 +40,9 @@ COPY --chown=node:node --from=builder /deploy/package.json ./package.json # dist is declared in package.json "files" so pnpm deploy copies it into /deploy; # copy from builder explicitly as belt-and-suspenders COPY --chown=node:node --from=builder /app/apps/gateway/dist ./dist +# The built web SPA bundle; served by the gateway (apps/gateway/src/spa/serve-spa.ts) +COPY --chown=node:node --from=builder /app/apps/web/dist ./web-dist +ENV WEB_DIST_DIR=/app/web-dist # gateway defaults to port 14242 (apps/gateway/src/main.ts) EXPOSE 14242 USER node diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcfe530e..f35c1d20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,9 @@ importers: '@fastify/helmet': specifier: ^13.0.2 version: 13.0.2 + '@fastify/static': + specifier: ^8.3.0 + version: 8.3.0 '@mariozechner/pi-ai': specifier: ^0.65.0 version: 0.65.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) @@ -122,7 +125,7 @@ importers: version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/platform-fastify': specifier: ^11.0.0 - version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + version: 11.1.16(@fastify/static@8.3.0)(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) '@nestjs/platform-socket.io': specifier: ^11.0.0 version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.16)(rxjs@7.8.2) @@ -262,9 +265,6 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.1 - next: - specifier: ^16.0.0 - version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: specifier: ^19.0.0 version: 19.2.4 @@ -789,10 +789,10 @@ importers: dependencies: '@mariozechner/pi-agent-core': specifier: ^0.63.1 - version: 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@3.25.76) + version: 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) '@mariozechner/pi-ai': specifier: ^0.63.1 - version: 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@3.25.76) + version: 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) '@sinclair/typebox': specifier: ^0.34.41 version: 0.34.48 @@ -1862,6 +1862,9 @@ packages: '@noble/hashes': optional: true + '@fastify/accept-negotiator@2.1.0': + resolution: {integrity: sha512-F3EVbzWt+xcnVaOHmWyIlpuFtbxOln7HDZQsh09MtMmMm/CipMayNt8hnIL8VQi54u2ZociDbf+iluGYkf7B1A==} + '@fastify/ajv-compiler@4.0.5': resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} @@ -1889,6 +1892,12 @@ packages: '@fastify/proxy-addr@5.1.0': resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} + '@fastify/send@4.1.1': + resolution: {integrity: sha512-BYo+EiaKwlxH+WetGk6hAs1d39iP0y1gqB8lGF/qwkJ9ZZ/cBY1vx5NvExb9Sc3yRMFjD5X4Eyh4e4+TzRkzdw==} + + '@fastify/static@8.3.0': + resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==} + '@google/genai@1.45.0': resolution: {integrity: sha512-+sNRWhKiRibVgc4OKi7aBJJ0A7RcoVD8tGG+eFkqxAWRjASDW+ktS9lLwTDnAxZICzCVoeAdu8dYLJVTX60N9w==} engines: {node: '>=20.0.0'} @@ -2080,6 +2089,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -2115,6 +2128,10 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@lukeed/ms@2.0.2': + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + '@lydell/node-pty-darwin-arm64@1.2.0-beta.3': resolution: {integrity: sha512-owcv+e1/OSu3bf9ZBdUQqJsQF888KyuSIiPYFNn0fLhgkhm9F3Pvha76Kj5mCPnodf7hh3suDe7upw7GPRXftQ==} cpu: [arm64] @@ -4601,6 +4618,10 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -5320,6 +5341,12 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -5615,6 +5642,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -6113,6 +6144,11 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -7777,12 +7813,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@anthropic-ai/sdk@0.73.0(zod@3.25.76)': - dependencies: - json-schema-to-ts: 3.1.1 - optionalDependencies: - zod: 3.25.76 - '@anthropic-ai/sdk@0.73.0(zod@4.3.6)': dependencies: json-schema-to-ts: 3.1.1 @@ -8786,6 +8816,8 @@ snapshots: optionalDependencies: '@noble/hashes': 2.0.1 + '@fastify/accept-negotiator@2.1.0': {} + '@fastify/ajv-compiler@4.0.5': dependencies: ajv: 8.18.0 @@ -8824,6 +8856,23 @@ snapshots: '@fastify/forwarded': 3.0.1 ipaddr.js: 2.3.0 + '@fastify/send@4.1.1': + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.1 + mime: 3.0.0 + + '@fastify/static@8.3.0': + dependencies: + '@fastify/accept-negotiator': 2.1.0 + '@fastify/send': 4.1.1 + content-disposition: 0.5.4 + fastify-plugin: 5.1.0 + fastq: 1.20.1 + glob: 11.1.0 + '@google/genai@1.45.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))': dependencies: google-auth-library: 10.6.1 @@ -8999,6 +9048,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/cliui@9.0.0': {} + '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 @@ -9036,6 +9087,8 @@ snapshots: '@lukeed/csprng@1.1.0': {} + '@lukeed/ms@2.0.2': {} + '@lydell/node-pty-darwin-arm64@1.2.0-beta.3': optional: true @@ -9124,18 +9177,6 @@ snapshots: - ws - zod - '@mariozechner/pi-agent-core@0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@3.25.76)': - dependencies: - '@mariozechner/pi-ai': 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@3.25.76) - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - aws-crt - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - '@mariozechner/pi-agent-core@0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)': dependencies: '@mariozechner/pi-ai': 0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) @@ -9184,30 +9225,6 @@ snapshots: - ws - zod - '@mariozechner/pi-ai@0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@3.25.76)': - dependencies: - '@anthropic-ai/sdk': 0.73.0(zod@3.25.76) - '@aws-sdk/client-bedrock-runtime': 3.1008.0 - '@google/genai': 1.45.0(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6)) - '@mistralai/mistralai': 1.14.1 - '@sinclair/typebox': 0.34.48 - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - chalk: 5.6.2 - openai: 6.26.0(ws@8.20.0)(zod@3.25.76) - partial-json: 0.1.7 - proxy-agent: 6.5.0 - undici: 7.24.6 - zod-to-json-schema: 3.25.1(zod@3.25.76) - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - aws-crt - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - '@mariozechner/pi-ai@0.63.2(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)': dependencies: '@anthropic-ai/sdk': 0.73.0(zod@4.3.6) @@ -9505,7 +9522,7 @@ snapshots: optionalDependencies: '@nestjs/websockets': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@nestjs/platform-socket.io@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/platform-fastify@11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': + '@nestjs/platform-fastify@11.1.16(@fastify/static@8.3.0)(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': dependencies: '@fastify/cors': 11.2.0 '@fastify/formbody': 8.0.2 @@ -9519,6 +9536,8 @@ snapshots: path-to-regexp: 8.3.0 reusify: 1.1.0 tslib: 2.8.1 + optionalDependencies: + '@fastify/static': 8.3.0 '@nestjs/platform-socket.io@11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.16)(rxjs@7.8.2)': dependencies: @@ -9556,7 +9575,8 @@ snapshots: optionalDependencies: '@nestjs/platform-socket.io': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.16)(rxjs@7.8.2) - '@next/env@16.1.6': {} + '@next/env@16.1.6': + optional: true '@next/swc-darwin-arm64@16.1.6': optional: true @@ -11136,6 +11156,7 @@ snapshots: '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 + optional: true '@swc/helpers@0.5.21': dependencies: @@ -11522,6 +11543,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.15)(lightningcss@1.33.0))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.19.15)(lightningcss@1.33.0) + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@24.12.0)(lightningcss@1.33.0))': dependencies: '@vitest/spy': 2.1.9 @@ -11703,7 +11732,8 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.7: {} + baseline-browser-mapping@2.10.7: + optional: true basic-ftp@5.2.0: {} @@ -11892,7 +11922,8 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001778: {} + caniuse-lite@1.0.30001778: + optional: true ccount@2.0.1: {} @@ -11966,7 +11997,8 @@ snapshots: slice-ansi: 5.0.0 string-width: 7.2.0 - client-only@0.0.1: {} + client-only@0.0.1: + optional: true cliui@7.0.4: dependencies: @@ -12012,6 +12044,10 @@ snapshots: consola@3.4.2: {} + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -12863,6 +12899,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.4 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + glob@13.0.6: dependencies: minimatch: 10.2.4 @@ -13192,6 +13237,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + jiti@2.6.1: {} jose@6.2.1: {} @@ -13795,6 +13844,8 @@ snapshots: mime@2.6.0: {} + mime@3.0.0: {} + mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} @@ -13911,6 +13962,7 @@ snapshots: transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + optional: true node-abi@3.89.0: dependencies: @@ -13994,11 +14046,6 @@ snapshots: dependencies: mimic-function: 5.0.1 - openai@6.26.0(ws@8.20.0)(zod@3.25.76): - optionalDependencies: - ws: 8.20.0 - zod: 3.25.76 - openai@6.26.0(ws@8.20.0)(zod@4.3.6): optionalDependencies: ws: 8.20.0 @@ -14252,9 +14299,10 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 + optional: true postcss@8.5.26: dependencies: @@ -14935,6 +14983,7 @@ snapshots: dependencies: client-only: 0.0.1 react: 19.2.4 + optional: true superagent@10.3.0: dependencies: @@ -15360,7 +15409,7 @@ snapshots: vitest@2.1.9(@types/node@22.19.15)(jsdom@29.0.0(@noble/hashes@2.0.1))(lightningcss@1.33.0): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@24.12.0)(lightningcss@1.33.0)) + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.15)(lightningcss@1.33.0)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9