From 8f88e645854fb3f4273a512d3a25a5c658f83a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 22 Jun 2026 16:08:45 +0200 Subject: [PATCH 1/3] Remove Next.js support entrypoints --- README.md | 156 +-------- bun.lock | 306 +++--------------- package.json | 20 +- src/cli/index.ts | 8 - src/next-js/handler.ts | 99 ------ src/next-js/instrumentation.ts | 6 - .../next15/node_modules/next/package.json | 4 - test/fixtures/next15/package.json | 7 - .../next16/node_modules/next/package.json | 4 - test/fixtures/next16/package.json | 7 - test/next-js-handler.test.ts | 21 -- 11 files changed, 48 insertions(+), 590 deletions(-) delete mode 100644 src/next-js/handler.ts delete mode 100644 src/next-js/instrumentation.ts delete mode 100644 test/fixtures/next15/node_modules/next/package.json delete mode 100644 test/fixtures/next15/package.json delete mode 100644 test/fixtures/next16/node_modules/next/package.json delete mode 100644 test/fixtures/next16/package.json delete mode 100644 test/next-js-handler.test.ts diff --git a/README.md b/README.md index 731ba80..2d00844 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ more information. This project supports: -- Next.js 15/16 - TanStack Start with React - Vite with React/Vue (which includes Astro, VitePress, etc) @@ -19,154 +18,6 @@ This project can also be used through the CLI or as ## Installation -### Next.js - -If you are using Next.js, install Tidewave with: - -```sh -$ npx tidewave install -# or -$ yarn dlx tidewave install -# or -$ pnpm dlx tidewave install -# or -$ bunx tidewave install -``` - -And you are almost there! Now make sure -[Tidewave is installed](https://hexdocs.pm/tidewave/installation.html) and you -are ready to connect Tidewave to your app. - -In case the command above do not work, you can toggle the manual installation -instructions below - -
-Show manual installation steps
- -**1. Add Tidewave as a dependency** - -```sh -$ npm install -D tidewave -# or -$ yarn add -D tidewave -# or -$ pnpm add --save-dev tidewave -# or -$ bun add --dev tidewave -``` - -**2. Create `pages/api/tidewave.ts`** - -Then create `pages/api/tidewave.ts` with: - -```typescript -import type { NextApiRequest, NextApiResponse } from 'next'; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse, -) { - if (process.env.NODE_ENV === 'development') { - const { tidewaveHandler } = await import('tidewave/next-js/handler'); - const handler = await tidewaveHandler(); - return handler(req, res); - } else { - res.status(404).end(); - } -} - -export const config = { - runtime: 'nodejs', - api: { - bodyParser: false, // Tidewave already parses the body internally - }, -}; -``` - -**3. Create the proxy.ts or middleware.ts** - -If you are using Next.js 16+, then create (or modify) `proxy.ts` with: - -```typescript -import { NextRequest, NextResponse } from 'next/server'; - -export function proxy(req: NextRequest): NextResponse { - if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - - // Here you could add your own logic or different middlewares. - return NextResponse.next(); -} -``` - -For Next.js 15+ and earlier, create (or modify) `middleware.ts` with: - -```typescript -import { NextRequest, NextResponse } from 'next/server'; - -export function middleware(req: NextRequest): NextResponse { - if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - - // Here you could add your own logic or different middlewares. - return NextResponse.next(); -} - -export const config = { - matcher: ['/tidewave/:path*'], -}; -``` - -**4. Create instrumentation.ts** - -Finally, we expose your application's spans, events, and logs to Tidewave MCP. -First install the NodeSDK: - -```sh -npm install @opentelemetry/sdk-node -npm install -D @opentelemetry/sdk-trace-base @opentelemetry/sdk-logs -``` - -And then create (or modify) a custom `instrumentation.ts` file in the root -directory of the project (or inside `src` folder if using one): - -```typescript -// instrumentation.ts -import type { SpanProcessor } from '@opentelemetry/sdk-trace-base'; -import type { LogRecordProcessor } from '@opentelemetry/sdk-logs'; - -export async function register() { - if (process.env.NEXT_RUNTIME === 'nodejs') { - const { NodeSDK } = await import('@opentelemetry/sdk-node'); - - // Add your app own processes here existing configuration - const sdkConfig: { - spanProcessors: SpanProcessor[]; - logRecordProcessors: LogRecordProcessor[]; - } = { - spanProcessors: [], - logRecordProcessors: [], - }; - - // Conditionally add Tidewave processors in development - if (process.env.NODE_ENV === 'development') { - const { TidewaveSpanProcessor, TidewaveLogRecordProcessor } = - await import('tidewave/next-js/instrumentation'); - - sdkConfig.spanProcessors.push(new TidewaveSpanProcessor()); - sdkConfig.logRecordProcessors.push(new TidewaveLogRecordProcessor()); - } - - const sdk = new NodeSDK(sdkConfig); - sdk.start(); - } -} -``` - -
- ### TanStack Start If you are using TanStack Start with React, install Tidewave with: @@ -255,8 +106,7 @@ features. ### Configuration -Next.js' `tidewaveHandler` and Vite's `tidewave` accept the configuration -options below: +Vite's `tidewave` accepts the configuration options below: - `allow_remote_access:` Tidewave only allows requests from localhost by default, even if your server listens on other interfaces, for security @@ -348,8 +198,8 @@ bun run clean # Clean dist directory ## Acknowledgements -A thank you to [Zoey](https://github.com/zoedsoupe/) for implementing both -Next.js and Vite integrations as well as the CLI interface. +A thank you to [Zoey](https://github.com/zoedsoupe/) for implementing the Vite +integration as well as the CLI interface. ## License diff --git a/bun.lock b/bun.lock index f964f10..c37684c 100644 --- a/bun.lock +++ b/bun.lock @@ -1,37 +1,33 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "tidewave", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", "body-parser": "^2.2.0", - "chalk": "^5.3.0", - "commander": "^12.1.0", "connect": "^3.7.0", "typescript": "^5", "zod": "3.25.76", }, "devDependencies": { "@eslint/js": "^9.16.0", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/sdk-logs": "^0.206.0", - "@opentelemetry/sdk-trace-base": "^2.1.0", "@types/body-parser": "^1.19.6", "@types/bun": "latest", "@types/connect": "^3.4.38", "@types/node": "^22.10.1", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", - "@vercel/otel": "^2.0.1", "@vitest/ui": "^3.2.4", "bun-types": "latest", + "chalk": "^5.3.0", + "commander": "^12.1.0", "eslint": "^9.16.0", "globals": "^15.14.0", - "next": "^15.5.3", "prettier": "^3.4.2", "vite": "^7.1.5", - "vitest": "^3.2.4", + "vitest": "^4.1.8", }, "peerDependencies": { "typescript": "^5", @@ -39,8 +35,6 @@ }, }, "packages": { - "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], @@ -119,98 +113,16 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], - - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA=="], - - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.3" }, "os": "darwin", "cpu": "x64" }, "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg=="], - - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw=="], - - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA=="], - - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA=="], - - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ=="], - - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg=="], - - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w=="], - - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg=="], - - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw=="], - - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g=="], - - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.3" }, "os": "linux", "cpu": "arm" }, "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA=="], - - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ=="], - - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ=="], - - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.3" }, "os": "linux", "cpu": "s390x" }, "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw=="], - - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A=="], - - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA=="], - - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg=="], - - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.4", "", { "dependencies": { "@emnapi/runtime": "^1.5.0" }, "cpu": "none" }, "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA=="], - - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA=="], - - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw=="], - - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.4", "", { "os": "win32", "cpu": "x64" }, "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.17.4", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw=="], - "@next/env": ["@next/env@15.5.3", "", {}, "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw=="], - - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg=="], - - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g=="], - - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw=="], - - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ=="], - - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.3", "", { "os": "linux", "cpu": "x64" }, "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA=="], - - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.3", "", { "os": "linux", "cpu": "x64" }, "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg=="], - - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA=="], - - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.3", "", { "os": "win32", "cpu": "x64" }, "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - - "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.206.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-yIVDu9jX//nV5wSMLZLdHdb1SKHIMj9k+wQVFtln5Flcgdldz9BkHtavvExQiJqBZg2OpEEJEZmzQazYztdz2A=="], - - "@opentelemetry/core": ["@opentelemetry/core@2.1.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ=="], - - "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.206.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.206.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-anPU9GAn3vSH/0JFQZ4e626xRw8p8R21kxM7xammFk9BRhfDw1IpgqvFMllbb+1MSHHEX9EiUqYHJyWo/B6KGA=="], - - "@opentelemetry/resources": ["@opentelemetry/resources@2.1.0", "", { "dependencies": { "@opentelemetry/core": "2.1.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw=="], - - "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.206.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.206.0", "@opentelemetry/core": "2.1.0", "@opentelemetry/resources": "2.1.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-SQ2yTmqe4Mw9RI3a/glVkfjWPsXh6LySvnljXubiZq4zu+UP8NMJt2j82ZsYb+KpD7Eu+/41/7qlJnjdeVjz7Q=="], - - "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.1.0", "", { "dependencies": { "@opentelemetry/core": "2.1.0", "@opentelemetry/resources": "2.1.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-J9QX459mzqHLL9Y6FZ4wQPRZG4TOpMCyPOh6mkr/humxE1W2S3Bvf4i75yiMW9uyed2Kf5rxmLhTm/UK8vNkAw=="], - - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.1.0", "", { "dependencies": { "@opentelemetry/core": "2.1.0", "@opentelemetry/resources": "2.1.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ=="], - - "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.37.0", "", {}, "sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA=="], - "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="], @@ -253,7 +165,7 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.46.2", "", { "os": "win32", "cpu": "x64" }, "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg=="], - "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], @@ -293,19 +205,17 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.39.1", "", { "dependencies": { "@typescript-eslint/types": "8.39.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A=="], - "@vercel/otel": ["@vercel/otel@2.0.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <2.0.0", "@opentelemetry/api-logs": ">=0.200.0 <0.300.0", "@opentelemetry/instrumentation": ">=0.200.0 <0.300.0", "@opentelemetry/resources": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-logs": ">=0.200.0 <0.300.0", "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0" } }, "sha512-eC8F8t9Ksn6xPBXBef7w2pMVcgjXoUOMJ3gnTs4Yh/zbKSIKfvX17GjyqCODwF9FdbiiPBxLsF8aQxFOtKyiEA=="], - - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + "@vitest/expect": ["@vitest/expect@4.1.9", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA=="], - "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + "@vitest/mocker": ["@vitest/mocker@4.1.9", "", { "dependencies": { "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw=="], - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.9", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A=="], - "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + "@vitest/runner": ["@vitest/runner@4.1.9", "", { "dependencies": { "@vitest/utils": "4.1.9", "pathe": "^2.0.3" } }, "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg=="], - "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + "@vitest/snapshot": ["@vitest/snapshot@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA=="], - "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + "@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="], "@vitest/ui": ["@vitest/ui@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.4" } }, "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA=="], @@ -315,8 +225,6 @@ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], @@ -325,8 +233,6 @@ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], @@ -339,26 +245,16 @@ "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001743", "", {}, "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw=="], - - "chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="], + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], "chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], - "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], - - "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -373,6 +269,8 @@ "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], @@ -385,14 +283,10 @@ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - "detect-libc": ["detect-libc@2.1.0", "", {}, "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], @@ -403,7 +297,7 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], @@ -437,7 +331,7 @@ "eventsource-parser": ["eventsource-parser@3.0.5", "", {}, "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ=="], - "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], @@ -503,8 +397,6 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "import-in-the-middle": ["import-in-the-middle@1.14.4", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -521,8 +413,6 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -541,7 +431,7 @@ "loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="], - "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -559,8 +449,6 @@ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], - "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -571,12 +459,12 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@15.5.3", "", { "dependencies": { "@next/env": "15.5.3", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.3", "@next/swc-darwin-x64": "15.5.3", "@next/swc-linux-arm64-gnu": "15.5.3", "@next/swc-linux-arm64-musl": "15.5.3", "@next/swc-linux-x64-gnu": "15.5.3", "@next/swc-linux-x64-musl": "15.5.3", "@next/swc-win32-arm64-msvc": "15.5.3", "@next/swc-win32-x64-msvc": "15.5.3", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + "obug": ["obug@2.1.3", "", {}, "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], @@ -599,15 +487,13 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="], - "postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -625,12 +511,6 @@ "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], - "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], - - "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], - - "require-in-the-middle": ["require-in-the-middle@8.0.0", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-9s0pnM5tH8G4dSI3pms2GboYOs25LwOGnRMxN/Hx3TYT1K0rh6OjaWf4dI0DAQnMyaEXWoGVnSTPQasqwzTTAA=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], @@ -645,8 +525,6 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], @@ -655,8 +533,6 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "sharp": ["sharp@0.34.4", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.0", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.4", "@img/sharp-darwin-x64": "0.34.4", "@img/sharp-libvips-darwin-arm64": "1.2.3", "@img/sharp-libvips-darwin-x64": "1.2.3", "@img/sharp-libvips-linux-arm": "1.2.3", "@img/sharp-libvips-linux-arm64": "1.2.3", "@img/sharp-libvips-linux-ppc64": "1.2.3", "@img/sharp-libvips-linux-s390x": "1.2.3", "@img/sharp-libvips-linux-x64": "1.2.3", "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", "@img/sharp-libvips-linuxmusl-x64": "1.2.3", "@img/sharp-linux-arm": "0.34.4", "@img/sharp-linux-arm64": "0.34.4", "@img/sharp-linux-ppc64": "0.34.4", "@img/sharp-linux-s390x": "0.34.4", "@img/sharp-linux-x64": "0.34.4", "@img/sharp-linuxmusl-arm64": "0.34.4", "@img/sharp-linuxmusl-x64": "0.34.4", "@img/sharp-wasm32": "0.34.4", "@img/sharp-win32-arm64": "0.34.4", "@img/sharp-win32-ia32": "0.34.4", "@img/sharp-win32-x64": "0.34.4" } }, "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -679,28 +555,20 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], + "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], - - "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], - "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - "tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], @@ -709,8 +577,6 @@ "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], @@ -729,9 +595,7 @@ "vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="], - "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "vitest": ["vitest@4.1.9", "", { "dependencies": { "@vitest/expect": "4.1.9", "@vitest/mocker": "4.1.9", "@vitest/pretty-format": "4.1.9", "@vitest/runner": "4.1.9", "@vitest/snapshot": "4.1.9", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.9", "@vitest/browser-preview": "4.1.9", "@vitest/browser-webdriverio": "4.1.9", "@vitest/coverage-istanbul": "4.1.9", "@vitest/coverage-v8": "4.1.9", "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -761,6 +625,18 @@ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@vitest/expect/@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="], + + "@vitest/expect/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "@vitest/pretty-format/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "@vitest/runner/@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="], + + "@vitest/snapshot/@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="], + + "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + "bun-types/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], "connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -785,13 +661,13 @@ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "vite-node/vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "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" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], + "vitest/@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="], + + "vitest/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "vitest/vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "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" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], + "vitest/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], "@types/body-parser/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], @@ -799,110 +675,14 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@vitest/runner/@vitest/utils/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "@vitest/snapshot/@vitest/utils/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + "bun-types/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], "connect/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - - "vite-node/vite/esbuild": ["esbuild@0.21.5", "", { "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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - - "vite-node/vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "vitest/vite/esbuild": ["esbuild@0.21.5", "", { "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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - - "vitest/vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "vite-node/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "vite-node/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "vite-node/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "vite-node/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "vite-node/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "vite-node/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "vite-node/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "vite-node/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "vite-node/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "vite-node/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "vite-node/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "vite-node/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "vite-node/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "vite-node/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "vite-node/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "vite-node/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "vite-node/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "vite-node/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - - "vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], } } diff --git a/package.json b/package.json index fa91228..13b4d96 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,13 @@ { "name": "tidewave", "version": "0.7.0", - "description": "Tidewave for JavaScript (Next.js, TanStack, Vite)", + "description": "Tidewave for JavaScript (TanStack, Vite)", "keywords": [ "typescript", "documentation", "mcp", "cli", "tidewave", - "next", "vite", "tanstack" ], @@ -37,16 +36,6 @@ "import": "./dist/vite-plugin.js", "require": "./dist/vite-plugin.js" }, - "./next-js/handler": { - "types": "./dist/next-js/handler.d.ts", - "import": "./dist/next-js/handler.js", - "require": "./dist/next-js/handler.js" - }, - "./next-js/instrumentation": { - "types": "./dist/next-js/instrumentation.d.ts", - "import": "./dist/next-js/instrumentation.js", - "require": "./dist/next-js/instrumentation.js" - }, "./tanstack": { "types": "./dist/tanstack.d.ts", "import": "./dist/tanstack.js", @@ -62,7 +51,7 @@ ], "scripts": { "dist": "bun run clean && bun run build:js && bun run build:types", - "build:js": "bun build --outdir dist --root src --external typescript --external child_process --external module --external @opentelemetry/api --external @opentelemetry/sdk-logs --external @opentelemetry/sdk-trace-base src/next-js/handler.ts src/next-js/instrumentation.ts src/vite-plugin.ts src/tanstack.ts src/evaluation/eval_worker.ts src/cli/index.ts --target node", + "build:js": "bun build --outdir dist --root src --external typescript --external child_process --external module src/vite-plugin.ts src/tanstack.ts src/evaluation/eval_worker.ts src/cli/index.ts --target node", "build:types": "tsc -p tsconfig.declarations.json", "dev": "bun run src/cli/index.ts", "test": "bun test", @@ -82,23 +71,18 @@ }, "devDependencies": { "@eslint/js": "^9.16.0", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/sdk-logs": "^0.206.0", - "@opentelemetry/sdk-trace-base": "^2.1.0", "@types/body-parser": "^1.19.6", "@types/bun": "latest", "@types/connect": "^3.4.38", "@types/node": "^22.10.1", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", - "@vercel/otel": "^2.0.1", "@vitest/ui": "^3.2.4", "bun-types": "latest", "chalk": "^5.3.0", "commander": "^12.1.0", "eslint": "^9.16.0", "globals": "^15.14.0", - "next": "^15.5.3", "prettier": "^3.4.2", "vite": "^7.1.5", "vitest": "^4.1.8" diff --git a/src/cli/index.ts b/src/cli/index.ts index b6dcd3c..815b1bc 100755 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -10,7 +10,6 @@ import { isExtractError, isResolveError } from '../core'; import { name, version } from '../../package.json'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { serveMcp } from '../mcp'; -import { handleInstall } from './install'; function chdir(path: string): void { try { @@ -79,13 +78,6 @@ const { source: { cli: sourceCli }, } = tools; -program - .command('install') - .description('Install Tidewave in a Next.js project') - .option('--prefix ', 'Specify the directory containing package.json') - .option('--dry-run', 'Preview changes without creating files') - .action(handleInstall); - program .command('mcp') .description('Starts a MCP server for tidewave (stdio)') diff --git a/src/next-js/handler.ts b/src/next-js/handler.ts deleted file mode 100644 index d283c7c..0000000 --- a/src/next-js/handler.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { checkSecurity, type Request, type Response } from '../http'; -import bodyParser from 'body-parser'; -import type { TidewaveConfig } from '../core'; -import { getProjectName } from '../core'; -import type { AddressInfo } from 'node:net'; - -const DEFAULT_CONFIG: TidewaveConfig = {}; - -type NextJsHandler = (_req: NextApiRequest, _res: NextApiResponse) => Promise; - -type NextHandler = () => ValueOrPromise; - -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -export type FunctionLike = (...args: any[]) => unknown; - -export type Nextable = ( - ...args: [...Parameters, NextHandler] -) => ValueOrPromise; /* eslint-disable-line @typescript-eslint/no-explicit-any */ - -export type ValueOrPromise = T | Promise; - -type NextFunction = (err?: unknown) => void; - -type ExpressRequestHandler = (req: Req, res: Res, next: NextFunction) => void; - -export type RequestHandler = ( - req: Req, - res: Res, -) => ValueOrPromise; - -function connectWrapper( - fn: ExpressRequestHandler, -): Nextable> { - return (req, res, next) => - new Promise((resolve, reject) => { - fn(req, res, err => (err ? reject(err) : resolve())); - }).then(next); -} - -function isAddressInfo(address: ReturnType): address is AddressInfo { - return typeof address === 'object' && address !== null && 'port' in address; -} - -export function getRequestLocalPort(request: Request): number | undefined { - const address = request.socket.address(); - - if (isAddressInfo(address)) return address.port; - - return request.socket.localPort; -} - -export async function tidewaveHandler( - config: TidewaveConfig = DEFAULT_CONFIG, -): Promise { - const env = process.env['NODE_ENV']; - - if (!(env === 'development')) { - throw Error( - `[Tidewave] tidewave is designed to only work on development environment, got: ${env}`, - ); - } - - if (process.env['NEXT_RUNTIME'] !== 'edge') { - // Import instrumentation to automatically patch console - const { patchConsole } = await import('../logger/console-patch'); - patchConsole(); - } - - // Set framework and projectName upfront - config.framework = 'nextjs'; - config.projectName = config.projectName || (await getProjectName('next_app')); - - return async function handler(req: NextApiRequest, res: NextApiResponse): Promise { - const origin = req.headers.host; - const url = new URL(req.url ?? '', `http://${origin}`); - const segments = url.pathname.split('/').filter(Boolean); - const [_tidewave, endpoint] = segments; - - if (!url.pathname.startsWith('/tidewave')) { - return res.status(404).json({ message: 'This route only works when accessed at /tidewave' }); - } - - const next: () => void = () => {}; - const securityMiddleware = checkSecurity(config); - await connectWrapper(securityMiddleware)(req, res, next); - await connectWrapper(bodyParser.json())(req, res, next); - - const { getHandlers } = await import('../http'); - const handlers = getHandlers(config, { - getLocalPort: getRequestLocalPort, - }); - - const handler = handlers[endpoint || '']; - if (handler) return await connectWrapper(handler)(req, res, next); - - return res.status(404).json({ message: `Route not found: ${req.method} ${req.url}` }); - }; -} diff --git a/src/next-js/instrumentation.ts b/src/next-js/instrumentation.ts deleted file mode 100644 index 9e0af0f..0000000 --- a/src/next-js/instrumentation.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { patchConsole } from '../logger/console-patch'; - -export { TidewaveSpanProcessor } from '../logger/tidewave-span-processor'; -export { TidewaveLogRecordProcessor } from '../logger/tidewave-log-record-processor'; - -patchConsole(); diff --git a/test/fixtures/next15/node_modules/next/package.json b/test/fixtures/next15/node_modules/next/package.json deleted file mode 100644 index 25824f9..0000000 --- a/test/fixtures/next15/node_modules/next/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "next", - "version": "15.5.3" -} diff --git a/test/fixtures/next15/package.json b/test/fixtures/next15/package.json deleted file mode 100644 index eb800fe..0000000 --- a/test/fixtures/next15/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "test-next15", - "version": "1.0.0", - "dependencies": { - "next": "^15.0.0" - } -} diff --git a/test/fixtures/next16/node_modules/next/package.json b/test/fixtures/next16/node_modules/next/package.json deleted file mode 100644 index 1315665..0000000 --- a/test/fixtures/next16/node_modules/next/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "next", - "version": "16.0.0" -} diff --git a/test/fixtures/next16/package.json b/test/fixtures/next16/package.json deleted file mode 100644 index 5c267d3..0000000 --- a/test/fixtures/next16/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "test-next16", - "version": "1.0.0", - "dependencies": { - "next": "^16.0.0" - } -} diff --git a/test/next-js-handler.test.ts b/test/next-js-handler.test.ts deleted file mode 100644 index fe78745..0000000 --- a/test/next-js-handler.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { getRequestLocalPort } from '../src/next-js/handler'; -import type { Request } from '../src/http'; - -describe('Tidewave Next.js Handler', () => { - it('should use the request socket port in config responses', () => { - const req = { - headers: { - host: 'localhost:3000', - origin: 'http://localhost:9999', - }, - socket: { - remoteAddress: '127.0.0.1', - localPort: 1234, - address: () => ({ address: '127.0.0.1', family: 'IPv4', port: 4321 }), - }, - }; - - expect(getRequestLocalPort(req as unknown as Request)).toBe(4321); - }); -}); From 5cd8630dd4d3fb2951aa41f686dd8560ec6957b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 22 Jun 2026 16:09:16 +0200 Subject: [PATCH 2/3] Remove unused Next.js internals --- bun.lock | 23 +- package.json | 1 - src/cli/install.ts | 408 -------------------- src/http/handlers/html.ts | 2 +- src/http/index.ts | 8 +- src/logger/tidewave-log-record-processor.ts | 40 -- src/logger/tidewave-span-processor.ts | 109 ------ test/install.test.ts | 225 ----------- 8 files changed, 4 insertions(+), 812 deletions(-) delete mode 100644 src/cli/install.ts delete mode 100644 src/logger/tidewave-log-record-processor.ts delete mode 100644 src/logger/tidewave-span-processor.ts delete mode 100644 test/install.test.ts diff --git a/bun.lock b/bun.lock index c37684c..96e2752 100644 --- a/bun.lock +++ b/bun.lock @@ -7,7 +7,6 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", "body-parser": "^2.2.0", - "connect": "^3.7.0", "typescript": "^5", "zod": "3.25.76", }, @@ -263,8 +262,6 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "connect": ["connect@3.7.0", "", { "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" } }, "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ=="], - "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], @@ -355,7 +352,7 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "finalhandler": ["finalhandler@1.1.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" } }, "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], @@ -589,8 +586,6 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="], @@ -639,24 +634,12 @@ "bun-types/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], - "connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "express/finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - - "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], - - "finalhandler/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="], - - "finalhandler/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], - "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -680,9 +663,5 @@ "@vitest/snapshot/@vitest/utils/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], "bun-types/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], - - "connect/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - - "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], } } diff --git a/package.json b/package.json index 13b4d96..1fb5626 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", "body-parser": "^2.2.0", - "connect": "^3.7.0", "typescript": "^5", "zod": "3.25.76" }, diff --git a/src/cli/install.ts b/src/cli/install.ts deleted file mode 100644 index 4c27744..0000000 --- a/src/cli/install.ts +++ /dev/null @@ -1,408 +0,0 @@ -// src/cli/install.ts -import * as fs from 'fs'; -import * as path from 'path'; -import * as child_process from 'child_process'; -import * as readline from 'readline'; -import chalk from 'chalk'; - -interface InstallOptions { - prefix?: string; - dryRun?: boolean; - skipDeps?: boolean; -} - -interface NextJsVersion { - major: number; - minor: number; - raw: string; -} - -export async function handleInstall(options: InstallOptions): Promise { - const targetDir = options.prefix || process.cwd(); - const dryRun = options.dryRun || false; - const skipDeps = options.skipDeps || false; - - console.log(chalk.blue('\n🌊 Tidewave Installer\n')); - console.log(chalk.gray(`Target directory: ${targetDir}`)); - - if (dryRun) { - console.log(chalk.yellow('\nāš ļø DRY RUN MODE - No files will be created\n')); - } - - // Step 1: Detect Next.js version - const nextVersion = await detectNextJsVersion(targetDir); - if (!nextVersion) { - console.error(chalk.red('\nāŒ Could not find Next.js in node_modules.')); - console.log(chalk.gray('\nThe installer only works for Next.js projects\n')); - process.exit(1); - } - - console.log(chalk.green(`āœ… Detected Next.js ${nextVersion.raw} (v${nextVersion.major})`)); - - // Step 2: Create files - const steps: (() => Promise)[] = [ - (): Promise => createApiHandler(targetDir, dryRun), - (): Promise => createMiddleware(targetDir, nextVersion, dryRun), - (): Promise => createInstrumentation(targetDir, dryRun), - ]; - - for (const step of steps) { - await step(); - } - - // Step 3: Install dependencies - if (!skipDeps) { - await installDependencies(targetDir, dryRun); - } - - // Summary - console.log(chalk.green('\nāœ… Tidewave setup complete!\n')); - console.log(chalk.blue('Next steps:')); - console.log(' 1. Start your Next.js dev server: npm run dev'); - console.log(' 2. Install the Tidewave app: https://hexdocs.pm/tidewave/'); - console.log(' 3. Connect to your app through Tidewave\n'); -} - -async function detectNextJsVersion(dir: string): Promise { - try { - // Get the actual installed version from node_modules - const nextPackageJsonPath = path.join(dir, 'node_modules', 'next', 'package.json'); - - if (!fs.existsSync(nextPackageJsonPath)) { - return null; - } - - const nextPackageJson = JSON.parse(fs.readFileSync(nextPackageJsonPath, 'utf-8')); - const { version } = nextPackageJson; - const match = version.match(/^(\d+)\.(\d+)\.(\d+)/); - - if (match) { - return { - major: parseInt(match[1]), - minor: parseInt(match[2]), - raw: version, - }; - } - - return null; - } catch { - return null; - } -} - -function detectPackageManager(dir: string): string { - // Check for lock files to determine package manager - if (fs.existsSync(path.join(dir, 'bun.lockb'))) return 'bun'; - if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm'; - if (fs.existsSync(path.join(dir, 'yarn.lock'))) return 'yarn'; - if (fs.existsSync(path.join(dir, 'package-lock.json'))) return 'npm'; - // Default to npm if no lock file found - return 'npm'; -} - -async function promptUser(question: string): Promise { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise(resolve => { - rl.question(question, answer => { - rl.close(); - const normalized = answer.trim().toLowerCase(); - // Default to yes if empty, or explicit yes/y - resolve(normalized === '' || normalized === 'y' || normalized === 'yes'); - }); - }); -} - -async function installDependencies(dir: string, dryRun: boolean): Promise { - console.log(chalk.yellow('šŸ“¦ Installing dependencies...')); - - if (dryRun) { - console.log(chalk.gray('[DRY RUN] Would install: tidewave, @opentelemetry/sdk-node')); - return; - } - - const pm = detectPackageManager(dir); - const devDepCommands: Record = { - npm: ['install', '-D', 'tidewave', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-logs'], - yarn: ['add', '-D', 'tidewave', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-logs'], - pnpm: [ - 'add', - '--save-dev', - 'tidewave', - '@opentelemetry/sdk-trace-base', - '@opentelemetry/sdk-logs', - ], - bun: ['add', '--dev', 'tidewave', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-logs'], - }; - - const depCommands: Record = { - npm: ['install', '@opentelemetry/sdk-node'], - yarn: ['add', '@opentelemetry/sdk-node'], - pnpm: ['add', '@opentelemetry/sdk-node'], - bun: ['add', '@opentelemetry/sdk-node'], - }; - - const devCmds = devDepCommands[pm]; - const depCmds = depCommands[pm]; - - if (!devCmds || !depCmds) { - console.error(chalk.red(`\nāŒ Unknown package manager: ${pm}`)); - process.exit(1); - } - - try { - // Install tidewave as dev dependency - child_process.execSync(`${pm} ${devCmds.join(' ')}`, { - cwd: dir, - stdio: 'inherit', - }); - - // Install OpenTelemetry as regular dependency - child_process.execSync(`${pm} ${depCmds.join(' ')}`, { - cwd: dir, - stdio: 'inherit', - }); - - console.log(chalk.green('āœ… Dependencies installed')); - } catch (_error) { - console.error(chalk.red('\nāŒ Failed to install dependencies')); - console.log( - chalk.gray( - `\nPlease install manually:\n ${pm} ${devCmds.join(' ')}\n ${pm} ${depCmds.join(' ')}`, - ), - ); - } -} - -async function createApiHandler(dir: string, dryRun: boolean): Promise { - const apiDir = path.join(dir, 'pages', 'api'); - const handlerPath = path.join(apiDir, 'tidewave.ts'); - - const handlerContent = `import type { NextApiRequest, NextApiResponse } from 'next'; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse, -) { - if (process.env.NODE_ENV === 'development') { - const { tidewaveHandler } = await import('tidewave/next-js/handler'); - const handler = await tidewaveHandler(); - return handler(req, res); - } else { - res.status(404).end(); - } -} - -export const config = { - runtime: 'nodejs', - api: { - bodyParser: false, // Tidewave already parses the body internally - }, -}; -`; - - if (fs.existsSync(handlerPath)) { - if (dryRun) { - console.log( - chalk.gray(`[DRY RUN] Would ask to overwrite: ${path.relative(dir, handlerPath)}`), - ); - return; - } - - const shouldOverwrite = await promptUser( - chalk.yellow(`\nāš ļø ${path.relative(dir, handlerPath)} already exists. Overwrite? (Y/n): `), - ); - - if (!shouldOverwrite) { - console.log(chalk.gray(`ā­ļø Skipping: ${path.relative(dir, handlerPath)}`)); - return; - } - } - - if (dryRun) { - console.log(chalk.gray(`[DRY RUN] Would create: ${path.relative(dir, handlerPath)}`)); - return; - } - - fs.mkdirSync(apiDir, { recursive: true }); - fs.writeFileSync(handlerPath, handlerContent, 'utf-8'); - console.log(chalk.green(`āœ… Created: ${path.relative(dir, handlerPath)}`)); -} - -async function createMiddleware( - dir: string, - nextVersion: NextJsVersion, - dryRun: boolean, -): Promise { - const isNext16Plus = nextVersion.major >= 16; - const fileName = isNext16Plus ? 'proxy.ts' : 'middleware.ts'; - const filePath = path.join(dir, fileName); - - if (fs.existsSync(filePath)) { - console.log(chalk.yellow(`ā­ļø ${fileName} already exists`)); - - // Check if Tidewave is already configured - const content = fs.readFileSync(filePath, 'utf-8'); - if (content.includes('/tidewave') && content.includes('/api/tidewave')) { - console.log(chalk.green(`āœ… ${fileName} already configured for Tidewave`)); - return; - } - - console.log(chalk.gray(`\n Please manually add the following to your ${fileName}:\n`)); - printMiddlewareInstructions(isNext16Plus); - return; - } - - const middlewareContent = isNext16Plus - ? `import { NextRequest, NextResponse } from 'next/server'; - -export function proxy(req: NextRequest): NextResponse { - if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - - // Here you could add your own logic or different middlewares. - return NextResponse.next(); -} -` - : `import { NextRequest, NextResponse } from 'next/server'; - -export function middleware(req: NextRequest): NextResponse { - if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - - // Here you could add your own logic or different middlewares. - return NextResponse.next(); -} - -export const config = { - matcher: ['/tidewave/:path*'], -}; -`; - - if (dryRun) { - console.log(chalk.gray(`[DRY RUN] Would create: ${fileName}`)); - return; - } - - fs.writeFileSync(filePath, middlewareContent, 'utf-8'); - console.log(chalk.green(`āœ… Created: ${fileName}`)); -} - -function printMiddlewareInstructions(isNext16Plus: boolean): void { - if (isNext16Plus) { - console.log( - chalk.cyan(` if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - }`), - ); - } else { - console.log( - chalk.cyan(` if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - - // Also add to config.matcher: - export const config = { - matcher: ['/tidewave/:path*'], - };`), - ); - } - console.log(); -} - -async function createInstrumentation(dir: string, dryRun: boolean): Promise { - // Check both root and src directories - const rootPath = path.join(dir, 'instrumentation.ts'); - const srcPath = path.join(dir, 'src', 'instrumentation.ts'); - const hasSrcDir = fs.existsSync(path.join(dir, 'src')); - - const instrumentationPath = hasSrcDir ? srcPath : rootPath; - - if (fs.existsSync(rootPath) || fs.existsSync(srcPath)) { - const existingPath = fs.existsSync(rootPath) ? rootPath : srcPath; - const existingFile = fs.existsSync(rootPath) ? 'instrumentation.ts' : 'src/instrumentation.ts'; - - console.log(chalk.yellow(`ā­ļø ${existingFile} already exists`)); - - // Check if Tidewave is already configured - const content = fs.readFileSync(existingPath, 'utf-8'); - if ( - content.includes('TidewaveSpanProcessor') && - content.includes('TidewaveLogRecordProcessor') - ) { - console.log(chalk.green(`āœ… ${existingFile} already configured for Tidewave`)); - return; - } - - console.log(chalk.gray('\n Please manually add the following to your instrumentation.ts:\n')); - printInstrumentationInstructions(); - return; - } - - const instrumentationContent = `import type { SpanProcessor } from "@opentelemetry/sdk-trace-base"; -import type { LogRecordProcessor } from "@opentelemetry/sdk-logs"; - -export async function register() { - if (process.env.NEXT_RUNTIME === "nodejs") { - const { NodeSDK } = await import("@opentelemetry/sdk-node"); - - // Add your app own processes here existing configuration - const sdkConfig: { - spanProcessors: SpanProcessor[]; - logRecordProcessors: LogRecordProcessor[]; - } = { - spanProcessors: [], - logRecordProcessors: [], - }; - - // Conditionally add Tidewave processors in development - if (process.env.NODE_ENV === "development") { - const { TidewaveSpanProcessor, TidewaveLogRecordProcessor } = await import( - "tidewave/next-js/instrumentation" - ); - - sdkConfig.spanProcessors.push(new TidewaveSpanProcessor()); - sdkConfig.logRecordProcessors.push(new TidewaveLogRecordProcessor()); - } - - const sdk = new NodeSDK(sdkConfig); - sdk.start(); - } -} -`; - - if (dryRun) { - console.log(chalk.gray(`[DRY RUN] Would create: ${path.relative(dir, instrumentationPath)}`)); - return; - } - - if (hasSrcDir) { - fs.mkdirSync(path.dirname(instrumentationPath), { recursive: true }); - } - - fs.writeFileSync(instrumentationPath, instrumentationContent, 'utf-8'); - console.log(chalk.green(`āœ… Created: ${path.relative(dir, instrumentationPath)}`)); -} - -function printInstrumentationInstructions(): void { - console.log( - chalk.cyan(` // Inside your register() function: - const runtime = process.env.NEXT_RUNTIME; - const env = process.env.NODE_ENV; - - if (runtime === 'nodejs' && env === 'development') { - const { TidewaveSpanProcessor, TidewaveLogRecordProcessor } = await import( - 'tidewave/next-js/instrumentation' - ); - - sdkConfig.spanProcessors.push(new TidewaveSpanProcessor()); - sdkConfig.logRecordProcessors.push(new TidewaveLogRecordProcessor()); - }`), - ); - console.log(); -} diff --git a/src/http/handlers/html.ts b/src/http/handlers/html.ts index 3092811..ca3f7fa 100644 --- a/src/http/handlers/html.ts +++ b/src/http/handlers/html.ts @@ -6,7 +6,7 @@ export function createHandleHtml(config: TidewaveConfig): Handler { const url = req.url || '/'; const [pathname] = url.split('?'); - // vite middleware strips the url, but next.js does not, so we check all three + // Different connect-style middleware stacks may strip different URL prefixes. if (pathname !== '' && pathname !== '/' && pathname !== '/tidewave') { return next(); } diff --git a/src/http/index.ts b/src/http/index.ts index 5b0f3b9..0d41b50 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -1,6 +1,5 @@ import type { ServerResponse } from 'http'; import type { IncomingMessage, NextFunction, Server } from 'connect'; -import connect from 'connect'; import { checkRemoteIp } from './security'; import { handleMcp } from './handlers/mcp'; import { createHandleHtml } from './handlers/html'; @@ -37,7 +36,7 @@ function getHandlers( } export function configureServer( - server: Server = connect(), + server: Server, config: TidewaveConfig = DEFAULT_OPTIONS, options: HandlerOptions = {}, ): Server { @@ -54,7 +53,7 @@ export function configureServer( return server; } -export function checkSecurity(config: TidewaveConfig) { +function checkSecurity(config: TidewaveConfig) { return (req: Request, res: Response, next: NextFn): void => { if (!checkRemoteIp(req, res, config)) return; next(); @@ -74,6 +73,3 @@ export function originNotAllowed(res: Response): void { res.statusCode = 403; res.end(message); } - -// Export for use by framework integrations -export { getHandlers }; diff --git a/src/logger/tidewave-log-record-processor.ts b/src/logger/tidewave-log-record-processor.ts deleted file mode 100644 index fa21426..0000000 --- a/src/logger/tidewave-log-record-processor.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { LogRecordProcessor, SdkLogRecord } from '@opentelemetry/sdk-logs'; -import type { Context } from '@opentelemetry/api'; -import { tidewaveLogger } from './tidewave-logger'; - -/** - * Custom LogRecordProcessor that captures OpenTelemetry logger logs - * and writes them directly to the tidewave logger. - * - * This is opt-in for users who want to capture actual logger logs - * (not console.log, which is handled separately by console patching). - */ -export class TidewaveLogRecordProcessor implements LogRecordProcessor { - /** - * Called when a log record is emitted. - */ - onEmit(logRecord: SdkLogRecord, _context?: Context): void { - try { - const body = String(logRecord.body || ''); - - tidewaveLogger.addLog({ - timestamp: new Date( - logRecord.hrTime[0] * 1000 + logRecord.hrTime[1] / 1_000_000, - ).toISOString(), - severityText: logRecord.severityText || 'INFO', - body, - attributes: logRecord.attributes, - }); - } catch (_error) { - // Silently fail to avoid breaking logging - } - } - - async forceFlush(): Promise { - // No-op: we process log records synchronously - } - - async shutdown(): Promise { - // No-op: no resources to clean up - } -} diff --git a/src/logger/tidewave-span-processor.ts b/src/logger/tidewave-span-processor.ts deleted file mode 100644 index d620e94..0000000 --- a/src/logger/tidewave-span-processor.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { SpanProcessor } from '@opentelemetry/sdk-trace-base'; -import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import type { Span, Context } from '@opentelemetry/api'; -import { tidewaveLogger } from './tidewave-logger'; - -/** - * Custom SpanProcessor that converts OpenTelemetry spans to logs and writes them - * directly to the tidewave logger. - * This is used to capture Next.js HTTP request/response spans and - * make them available via the get_logs tool. - */ -export class TidewaveSpanProcessor implements SpanProcessor { - /** - * Called when a span is started. We don't need to do anything here. - */ - onStart(_span: Span, _parentContext: Context): void { - // No-op: we only care about completed spans - } - - /** - * Called when a span ends. This is where we convert the span to a log entry - * and write it directly to the tidewave logger. - */ - onEnd(span: ReadableSpan): void { - try { - const spanType = span.attributes['next.span_type']; - const httpMethod = span.attributes['http.method']; - - // Only log certain span types to avoid noise - const relevantSpanTypes = [ - 'BaseServer.handleRequest', - 'AppRender.getBodyResult', - 'AppRouteRouteHandlers.runHandler', - ]; - - if (!spanType || !relevantSpanTypes.includes(spanType as string)) { - return; - } - - // Extract relevant information - const route = span.attributes['next.route'] || span.attributes['http.route']; - const httpUrl = span.attributes['http.url']; - const httpTarget = span.attributes['http.target']; - const httpStatusCode = span.attributes['http.status_code']; - - // Filter out /tidewave paths - const path = route || httpTarget || httpUrl || 'unknown'; - if (typeof path === 'string' && path.startsWith('/tidewave')) { - return; - } - - const durationMs = this.calculateDuration(span); - - let message = ''; - let severity = 'INFO'; - - if (spanType === 'BaseServer.handleRequest') { - const method = httpMethod || 'UNKNOWN'; - const status = httpStatusCode || 'unknown'; - message = `${method} ${path} ${status} ${durationMs.toFixed(2)}ms`; - - if (typeof httpStatusCode === 'number') { - if (httpStatusCode >= 500) severity = 'ERROR'; - else if (httpStatusCode >= 400) severity = 'WARN'; - } - } else if (spanType === 'AppRender.getBodyResult') { - message = `Rendered route: ${route || 'unknown'} (${durationMs.toFixed(2)}ms)`; - } else if (spanType === 'AppRouteRouteHandlers.runHandler') { - message = `API route: ${route || 'unknown'} (${durationMs.toFixed(2)}ms)`; - } - - // Write directly to tidewave logger - tidewaveLogger.addLog({ - timestamp: new Date(span.endTime[0] * 1000 + span.endTime[1] / 1_000_000).toISOString(), - severityText: severity, - body: message, - attributes: { - 'log.origin': 'opentelemetry-span', - 'span.name': span.name, - 'span.kind': span.kind, - 'span.type': spanType, - 'http.method': httpMethod, - 'http.route': route, - 'http.status_code': httpStatusCode, - 'duration.ms': durationMs, - }, - }); - } catch (_error) { - // Silently fail to avoid breaking tracing - } - } - - /** - * Calculate span duration in milliseconds from high-resolution time - */ - private calculateDuration(span: ReadableSpan): number { - const startTimeMs = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000; - const endTimeMs = span.endTime[0] * 1000 + span.endTime[1] / 1_000_000; - return endTimeMs - startTimeMs; - } - - async forceFlush(): Promise { - // No-op: we process spans synchronously - } - - async shutdown(): Promise { - // No-op: no resources to clean up - } -} diff --git a/test/install.test.ts b/test/install.test.ts deleted file mode 100644 index 1c95200..0000000 --- a/test/install.test.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { describe, test, expect, beforeEach, afterEach } from 'vitest'; -import * as fs from 'fs'; -import * as path from 'path'; -import { handleInstall } from '../src/cli/install'; - -const TEST_FIXTURES = path.join(__dirname, 'fixtures'); -const NEXT15_FIXTURE = path.join(TEST_FIXTURES, 'next15'); -const NEXT16_FIXTURE = path.join(TEST_FIXTURES, 'next16'); - -// Helper to clean up created files -function cleanupFiles(dir: string) { - const filesToRemove = [ - path.join(dir, 'pages/api/tidewave.ts'), - path.join(dir, 'middleware.ts'), - path.join(dir, 'proxy.ts'), - path.join(dir, 'instrumentation.ts'), - path.join(dir, 'src/instrumentation.ts'), - ]; - - for (const file of filesToRemove) { - if (fs.existsSync(file)) { - fs.unlinkSync(file); - } - } - - // Clean up directories if empty - const dirsToRemove = [ - path.join(dir, 'pages/api'), - path.join(dir, 'pages'), - path.join(dir, 'src'), - ]; - - for (const dirPath of dirsToRemove) { - if (fs.existsSync(dirPath)) { - try { - fs.rmdirSync(dirPath); - } catch { - // Directory not empty, skip - } - } - } -} - -describe('install command', () => { - describe('Next.js 15', () => { - beforeEach(() => { - cleanupFiles(NEXT15_FIXTURE); - }); - - afterEach(() => { - cleanupFiles(NEXT15_FIXTURE); - }); - - test('should create middleware.ts for Next.js 15', async () => { - await handleInstall({ - prefix: NEXT15_FIXTURE, - skipDeps: true, - }); - - const middlewarePath = path.join(NEXT15_FIXTURE, 'middleware.ts'); - expect(fs.existsSync(middlewarePath)).toBe(true); - - const content = fs.readFileSync(middlewarePath, 'utf-8'); - expect(content).toContain('export function middleware'); - expect(content).toContain('/tidewave'); - expect(content).toContain('/api/tidewave'); - expect(content).toContain("matcher: ['/tidewave/:path*']"); - }); - - test('should create pages/api/tidewave.ts', async () => { - await handleInstall({ - prefix: NEXT15_FIXTURE, - skipDeps: true, - }); - - const handlerPath = path.join(NEXT15_FIXTURE, 'pages/api/tidewave.ts'); - expect(fs.existsSync(handlerPath)).toBe(true); - - const content = fs.readFileSync(handlerPath, 'utf-8'); - expect(content).toContain('tidewaveHandler'); - expect(content).toContain('NextApiRequest'); - expect(content).toContain('NextApiResponse'); - expect(content).toContain('bodyParser: false'); - }); - - test('should create instrumentation.ts', async () => { - await handleInstall({ - prefix: NEXT15_FIXTURE, - skipDeps: true, - }); - - const instrumentationPath = path.join(NEXT15_FIXTURE, 'instrumentation.ts'); - expect(fs.existsSync(instrumentationPath)).toBe(true); - - const content = fs.readFileSync(instrumentationPath, 'utf-8'); - expect(content).toContain('export async function register'); - expect(content).toContain('TidewaveSpanProcessor'); - expect(content).toContain('TidewaveLogRecordProcessor'); - expect(content).toContain('NodeSDK'); - }); - }); - - describe('Next.js 16', () => { - beforeEach(() => { - cleanupFiles(NEXT16_FIXTURE); - }); - - afterEach(() => { - cleanupFiles(NEXT16_FIXTURE); - }); - - test('should create proxy.ts for Next.js 16', async () => { - await handleInstall({ - prefix: NEXT16_FIXTURE, - skipDeps: true, - }); - - const proxyPath = path.join(NEXT16_FIXTURE, 'proxy.ts'); - expect(fs.existsSync(proxyPath)).toBe(true); - - const content = fs.readFileSync(proxyPath, 'utf-8'); - expect(content).toContain('export function proxy'); - expect(content).toContain('/tidewave'); - expect(content).toContain('/api/tidewave'); - // Next.js 16 proxy should NOT have matcher config - expect(content).not.toContain('matcher'); - }); - - test('should create pages/api/tidewave.ts', async () => { - await handleInstall({ - prefix: NEXT16_FIXTURE, - skipDeps: true, - }); - - const handlerPath = path.join(NEXT16_FIXTURE, 'pages/api/tidewave.ts'); - expect(fs.existsSync(handlerPath)).toBe(true); - - const content = fs.readFileSync(handlerPath, 'utf-8'); - expect(content).toContain('tidewaveHandler'); - }); - - test('should create instrumentation.ts', async () => { - await handleInstall({ - prefix: NEXT16_FIXTURE, - skipDeps: true, - }); - - const instrumentationPath = path.join(NEXT16_FIXTURE, 'instrumentation.ts'); - expect(fs.existsSync(instrumentationPath)).toBe(true); - - const content = fs.readFileSync(instrumentationPath, 'utf-8'); - expect(content).toContain('export async function register'); - expect(content).toContain('TidewaveSpanProcessor'); - expect(content).toContain('TidewaveLogRecordProcessor'); - }); - }); - - describe('Existing files', () => { - beforeEach(() => { - cleanupFiles(NEXT16_FIXTURE); - }); - - afterEach(() => { - cleanupFiles(NEXT16_FIXTURE); - }); - - test('should skip existing middleware files if already configured', async () => { - // Create a proxy.ts that's already configured - const proxyPath = path.join(NEXT16_FIXTURE, 'proxy.ts'); - fs.writeFileSync( - proxyPath, - `import { NextRequest, NextResponse } from 'next/server'; - -export function proxy(req: NextRequest): NextResponse { - if (req.nextUrl.pathname.startsWith('/tidewave')) { - return NextResponse.rewrite(new URL('/api/tidewave', req.url)); - } - return NextResponse.next(); -} -`, - 'utf-8', - ); - - await handleInstall({ - prefix: NEXT16_FIXTURE, - skipDeps: true, - }); - - // File should still exist and be unchanged - const content = fs.readFileSync(proxyPath, 'utf-8'); - expect(content).toContain('export function proxy'); - expect(content).toContain('/tidewave'); - }); - - test('should skip existing instrumentation files if already configured', async () => { - // Create an instrumentation.ts that's already configured - const instrumentationPath = path.join(NEXT16_FIXTURE, 'instrumentation.ts'); - fs.writeFileSync( - instrumentationPath, - `import { NodeSDK } from '@opentelemetry/sdk-node'; -import { TidewaveSpanProcessor, TidewaveLogRecordProcessor } from 'tidewave/next-js/instrumentation'; - -export async function register() { - const sdk = new NodeSDK({ - spanProcessors: [new TidewaveSpanProcessor()], - logRecordProcessors: [new TidewaveLogRecordProcessor()], - }); - sdk.start(); -} -`, - 'utf-8', - ); - - await handleInstall({ - prefix: NEXT16_FIXTURE, - skipDeps: true, - }); - - // File should still exist and be unchanged - const content = fs.readFileSync(instrumentationPath, 'utf-8'); - expect(content).toContain('TidewaveSpanProcessor'); - expect(content).toContain('TidewaveLogRecordProcessor'); - }); - }); -}); From a6fc51b61919f1f19fb44e92d27de2b55862af96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 22 Jun 2026 16:10:10 +0200 Subject: [PATCH 3/3] Refactor HTTP middleware types --- bun.lock | 1 - package.json | 1 - src/http/handlers/config.ts | 14 +++++---- src/http/handlers/html.ts | 11 +++++-- src/http/handlers/mcp.ts | 24 ++++++++++++++-- src/http/index.ts | 57 +++++++++---------------------------- src/http/security.ts | 25 ++++++++++------ src/http/types.ts | 17 +++++++++++ test/http/index.test.ts | 43 +++++++++++----------------- test/http/security.test.ts | 12 ++++---- test/vite-plugin.test.ts | 16 +++++------ 11 files changed, 116 insertions(+), 105 deletions(-) create mode 100644 src/http/types.ts diff --git a/bun.lock b/bun.lock index 96e2752..431633d 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,6 @@ "@eslint/js": "^9.16.0", "@types/body-parser": "^1.19.6", "@types/bun": "latest", - "@types/connect": "^3.4.38", "@types/node": "^22.10.1", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", diff --git a/package.json b/package.json index 1fb5626..d398c48 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "@eslint/js": "^9.16.0", "@types/body-parser": "^1.19.6", "@types/bun": "latest", - "@types/connect": "^3.4.38", "@types/node": "^22.10.1", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", diff --git a/src/http/handlers/config.ts b/src/http/handlers/config.ts index 4b211a4..107df9a 100644 --- a/src/http/handlers/config.ts +++ b/src/http/handlers/config.ts @@ -1,21 +1,25 @@ -import { type Request, type Response, type NextFn, type Handler } from '../index'; import type { TidewaveConfig } from '../../core'; +import type { TidewaveHandler, TidewaveNext, TidewaveRequest, TidewaveResponse } from '../types'; import { default as tidewavePackage } from '../../../package.json' with { type: 'json' }; -export type LocalPortGetter = (req: Request) => number | undefined; +export type LocalPortGetter = () => number | undefined; export function createHandleConfig( config: TidewaveConfig, getLocalPort?: LocalPortGetter, -): Handler { - return async function handleConfig(req: Request, res: Response, next: NextFn): Promise { +): TidewaveHandler { + return async function handleConfig( + _req: TidewaveRequest, + res: TidewaveResponse, + next: TidewaveNext, + ): Promise { try { const tidewaveConfig = { project_name: config.projectName || 'app', framework_type: config.framework || 'unknown', tidewave_version: tidewavePackage.version, team: config.team || {}, - local_port: getLocalPort?.(req), + local_port: getLocalPort?.(), }; res.statusCode = 200; diff --git a/src/http/handlers/html.ts b/src/http/handlers/html.ts index ca3f7fa..a7c6c6e 100644 --- a/src/http/handlers/html.ts +++ b/src/http/handlers/html.ts @@ -1,7 +1,12 @@ -import type { Request, Response, NextFn, Handler } from '../index'; import type { TidewaveConfig } from '../../core'; -export function createHandleHtml(config: TidewaveConfig): Handler { - return async function handleHtml(req: Request, res: Response, next: NextFn): Promise { +import type { TidewaveHandler, TidewaveNext, TidewaveRequest, TidewaveResponse } from '../types'; + +export function createHandleHtml(config: TidewaveConfig): TidewaveHandler { + return async function handleHtml( + req: TidewaveRequest, + res: TidewaveResponse, + next: TidewaveNext, + ): Promise { // Only handle exact /tidewave path, not sub-paths const url = req.url || '/'; const [pathname] = url.split('?'); diff --git a/src/http/handlers/mcp.ts b/src/http/handlers/mcp.ts index 5eff29a..03b3577 100644 --- a/src/http/handlers/mcp.ts +++ b/src/http/handlers/mcp.ts @@ -1,8 +1,12 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; -import { methodNotAllowed, originNotAllowed, type Request, type Response, type NextFn } from '..'; import { serveMcp } from '../../mcp'; +import type { TidewaveNext, TidewaveRequest, TidewaveResponse } from '../types'; -export async function handleMcp(req: Request, res: Response, next: NextFn): Promise { +export async function handleMcp( + req: TidewaveRequest, + res: TidewaveResponse, + next: TidewaveNext, +): Promise { try { if (req.headers.origin) { originNotAllowed(res); @@ -38,7 +42,7 @@ export async function handleMcp(req: Request, res: Response, next: NextFn): Prom } } -function mcpErrorResponse(res: Response, err: Error | unknown): void { +function mcpErrorResponse(res: TidewaveResponse, err: Error | unknown): void { res.statusCode = 500; res.setHeader('content-type', 'application/json'); res.end( @@ -53,3 +57,17 @@ function mcpErrorResponse(res: Response, err: Error | unknown): void { }), ); } + +function methodNotAllowed(res: TidewaveResponse): void { + res.statusCode = 405; + res.setHeader('Allow', 'POST'); + res.end(); +} + +function originNotAllowed(res: TidewaveResponse): void { + const message = + 'For security reasons, Tidewave does not accept requests with an origin header for this endpoint.'; + console.warn(message); + res.statusCode = 403; + res.end(message); +} diff --git a/src/http/index.ts b/src/http/index.ts index 0d41b50..a83af1a 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -1,75 +1,44 @@ -import type { ServerResponse } from 'http'; -import type { IncomingMessage, NextFunction, Server } from 'connect'; import { checkRemoteIp } from './security'; import { handleMcp } from './handlers/mcp'; import { createHandleHtml } from './handlers/html'; import { createHandleConfig, type LocalPortGetter } from './handlers/config'; import bodyParser from 'body-parser'; import type { TidewaveConfig } from '../core'; - -export interface Request extends IncomingMessage { - body?: Record; -} -export type Response = ServerResponse; -export type NextFn = NextFunction; +import type { + TidewaveMiddlewareServer, + TidewaveNext, + TidewaveRequest, + TidewaveResponse, +} from './types'; export const ENDPOINT = '/tidewave' as const; const DEFAULT_OPTIONS: TidewaveConfig = { allowRemoteAccess: false, } as const; -export type Handler = (req: Request, res: Response, next: NextFn) => Promise; - export interface HandlerOptions { getLocalPort?: LocalPortGetter; } -function getHandlers( - config: TidewaveConfig, - options: HandlerOptions = {}, -): Record { - return { - '': createHandleHtml(config), - config: createHandleConfig(config, options.getLocalPort), - mcp: handleMcp, - }; -} - export function configureServer( - server: Server, + server: TidewaveMiddlewareServer, config: TidewaveConfig = DEFAULT_OPTIONS, options: HandlerOptions = {}, -): Server { +): TidewaveMiddlewareServer { const securityChecker = checkSecurity(config); server.use(`${ENDPOINT}`, securityChecker); - server.use(`${ENDPOINT}`, bodyParser.json()); - - const handlers = getHandlers(config, options); - for (const [path, handler] of Object.entries(handlers)) { - server.use(ENDPOINT + '/' + path, handler); - } + server.use(`${ENDPOINT}/`, createHandleHtml(config)); + server.use(`${ENDPOINT}/config`, createHandleConfig(config, options.getLocalPort)); + server.use(`${ENDPOINT}/mcp`, bodyParser.json()); + server.use(`${ENDPOINT}/mcp`, handleMcp); return server; } function checkSecurity(config: TidewaveConfig) { - return (req: Request, res: Response, next: NextFn): void => { + return (req: TidewaveRequest, res: TidewaveResponse, next: TidewaveNext): void => { if (!checkRemoteIp(req, res, config)) return; next(); }; } - -export function methodNotAllowed(res: Response): void { - res.statusCode = 405; - res.setHeader('Allow', 'POST'); - res.end(); -} - -export function originNotAllowed(res: Response): void { - const message = - 'For security reasons, Tidewave does not accept requests with an origin header for this endpoint.'; - console.warn(message); - res.statusCode = 403; - res.end(message); -} diff --git a/src/http/security.ts b/src/http/security.ts index 168448f..86006a5 100644 --- a/src/http/security.ts +++ b/src/http/security.ts @@ -1,21 +1,30 @@ import type { TidewaveConfig } from '../core'; -import type { Request, Response } from './index'; +import type { TidewaveRequest, TidewaveResponse } from './types'; -function fetchRemoteIp(req: Request): string | null { +function fetchRemoteIp(req: TidewaveRequest): string | null { const remote = req.socket.remoteAddress; if (remote) return remote; - const ip = (req.headers['x-real-ip'] && req.headers['x-forwarded-for']) || null; + const realIp = firstHeaderValue(req.headers['x-real-ip']); + if (realIp) return realIp; - if (Array.isArray(ip)) { - return ip.join(); - } + const forwardedFor = firstHeaderValue(req.headers['x-forwarded-for']); + if (forwardedFor) return forwardedFor.split(',')[0]?.trim() || null; - return ip; + return null; } -export function checkRemoteIp(req: Request, res: Response, config: TidewaveConfig): boolean { +function firstHeaderValue(value: string | string[] | undefined): string | null { + if (Array.isArray(value)) return value[0] || null; + return value || null; +} + +export function checkRemoteIp( + req: TidewaveRequest, + res: TidewaveResponse, + config: TidewaveConfig, +): boolean { const ip = fetchRemoteIp(req); if (!ip) return false; diff --git a/src/http/types.ts b/src/http/types.ts new file mode 100644 index 0000000..e7ed8a8 --- /dev/null +++ b/src/http/types.ts @@ -0,0 +1,17 @@ +import type { IncomingMessage, ServerResponse } from 'node:http'; + +export interface TidewaveRequest extends IncomingMessage { + body?: Record; +} + +export type TidewaveResponse = ServerResponse; +export type TidewaveNext = (err?: unknown) => void; +export type TidewaveHandler = ( + req: TidewaveRequest, + res: TidewaveResponse, + next: TidewaveNext, +) => Promise; + +export interface TidewaveMiddlewareServer { + use(route: string, handler: unknown): void; +} diff --git a/test/http/index.test.ts b/test/http/index.test.ts index 6aad560..64e3f56 100644 --- a/test/http/index.test.ts +++ b/test/http/index.test.ts @@ -1,11 +1,10 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { methodNotAllowed } from '../../src/http'; -import type { Request, Response } from '../../src/http'; +import type { TidewaveRequest, TidewaveResponse } from '../../src/http/types'; import { handleMcp } from '../../src/http/handlers/mcp'; import { createHandleConfig } from '../../src/http/handlers/config'; // Mock request/response helpers -const createMockRequest = (headers: Record = {}): Partial => ({ +const createMockRequest = (headers: Record = {}): Partial => ({ socket: { remoteAddress: '127.0.0.1' } as any, headers, }); @@ -21,7 +20,7 @@ const createMockResponse = () => { setHeader: mockSetHeader, headersSent: false, destroyed: false, - } as Partial, + } as Partial, mockEnd, mockSetHeader, }; @@ -34,37 +33,29 @@ describe('HTTP Utilities', () => { console.error = vi.fn(); }); - describe('methodNotAllowed', () => { - it('should set correct status code and headers', () => { - const { res, mockEnd, mockSetHeader } = createMockResponse(); - - methodNotAllowed(res as Response); - - expect(res.statusCode).toBe(405); - expect(mockSetHeader).toHaveBeenCalledWith('Allow', 'POST'); - expect(mockEnd).toHaveBeenCalled(); - }); - - it('should return undefined', () => { - const { res } = createMockResponse(); - - const result = methodNotAllowed(res as Response); - - expect(result).toBeUndefined(); - }); - }); - describe('handleMcp', () => { it('should return 403 if origin header is set', async () => { const req = createMockRequest({ origin: 'http://localhost:4000' }); const { res, mockEnd } = createMockResponse(); const next = vi.fn(); - await handleMcp(req as Request, res as Response, next); + await handleMcp(req as TidewaveRequest, res as TidewaveResponse, next); expect(res.statusCode).toBe(403); expect(mockEnd).toHaveBeenCalledWith(expect.stringContaining('origin')); }); + + it('should return 405 for non-POST requests', async () => { + const req = { ...createMockRequest(), method: 'GET' }; + const { res, mockEnd, mockSetHeader } = createMockResponse(); + const next = vi.fn(); + + await handleMcp(req as TidewaveRequest, res as TidewaveResponse, next); + + expect(res.statusCode).toBe(405); + expect(mockSetHeader).toHaveBeenCalledWith('Allow', 'POST'); + expect(mockEnd).toHaveBeenCalled(); + }); }); describe('handleConfig', () => { @@ -80,7 +71,7 @@ describe('HTTP Utilities', () => { }, () => 5173, ); - await handler(req as Request, res as Response, next); + await handler(req as TidewaveRequest, res as TidewaveResponse, next); expect(res.statusCode).toBe(200); expect(mockSetHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); diff --git a/test/http/security.test.ts b/test/http/security.test.ts index 9f74010..2796086 100644 --- a/test/http/security.test.ts +++ b/test/http/security.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { checkRemoteIp, isLocalIp } from '../../src/http/security'; -import type { Request, Response } from '../../src/http'; +import type { TidewaveRequest, TidewaveResponse } from '../../src/http/types'; import type { TidewaveConfig } from '../../src/core'; // Mock request/response helpers -const createMockRequest = (remoteAddress = '127.0.0.1'): Partial => ({ +const createMockRequest = (remoteAddress = '127.0.0.1'): Partial => ({ socket: { remoteAddress } as any, headers: {}, }); @@ -20,7 +20,7 @@ const createMockResponse = () => { setHeader: mockSetHeader, headersSent: false, destroyed: false, - } as Partial, + } as Partial, mockEnd, mockSetHeader, }; @@ -70,7 +70,7 @@ describe('HTTP Security', () => { const { res } = createMockResponse(); const config: TidewaveConfig = {}; - const result = checkRemoteIp(req as Request, res as Response, config); + const result = checkRemoteIp(req as TidewaveRequest, res as TidewaveResponse, config); expect(result).toBe(true); expect(res.statusCode).toBe(200); @@ -81,7 +81,7 @@ describe('HTTP Security', () => { const { res, mockEnd } = createMockResponse(); const config: TidewaveConfig = {}; - const result = checkRemoteIp(req as Request, res as Response, config); + const result = checkRemoteIp(req as TidewaveRequest, res as TidewaveResponse, config); expect(result).toBe(false); expect(res.statusCode).toBe(403); @@ -93,7 +93,7 @@ describe('HTTP Security', () => { const { res } = createMockResponse(); const config: TidewaveConfig = { allowRemoteAccess: true }; - const result = checkRemoteIp(req as Request, res as Response, config); + const result = checkRemoteIp(req as TidewaveRequest, res as TidewaveResponse, config); expect(result).toBe(true); expect(res.statusCode).toBe(200); diff --git a/test/vite-plugin.test.ts b/test/vite-plugin.test.ts index b431792..d2753bc 100644 --- a/test/vite-plugin.test.ts +++ b/test/vite-plugin.test.ts @@ -72,7 +72,7 @@ describe('Tidewave Vite Plugin', () => { const middlewareUse = (mockServer as any)._middlewareUse; - // Should register 5 middleware: security + decode body + html + config + mcp routes + // Should register 5 middleware: security + html + config + MCP body parser + MCP routes expect(middlewareUse).toHaveBeenCalledTimes(5); // Global security middleware @@ -109,7 +109,7 @@ describe('Tidewave Vite Plugin', () => { await (plugin.configureServer as any)(mockServer); - const configHandler = (mockServer as any)._middlewareUse.mock.calls[3][1]; + const configHandler = (mockServer as any)._middlewareUse.mock.calls[2][1]; const req = { socket: { remoteAddress: '127.0.0.1' }, headers: { @@ -208,16 +208,16 @@ describe('Tidewave Vite Plugin', () => { expect(calls[0][0]).toBe('/tidewave'); expect(typeof calls[0][1]).toBe('function'); - // Second call should be decode body middleware - expect(calls[1][0]).toBe('/tidewave'); + // Second call should be HTML endpoint (empty string = '/') + expect(calls[1][0]).toBe('/tidewave/'); expect(typeof calls[1][1]).toBe('function'); - // Third call should be HTML endpoint (empty string = '/') - expect(calls[2][0]).toBe('/tidewave/'); + // Third call should be config endpoint + expect(calls[2][0]).toBe('/tidewave/config'); expect(typeof calls[2][1]).toBe('function'); - // Fourth call should be config endpoint - expect(calls[3][0]).toBe('/tidewave/config'); + // Fourth call should parse JSON only for the MCP endpoint + expect(calls[3][0]).toBe('/tidewave/mcp'); expect(typeof calls[3][1]).toBe('function'); // Fifth call should be MCP endpoint