diff --git a/examples/clients/typescript/sse-retry-test.ts b/examples/clients/typescript/sse-retry-test.ts new file mode 100644 index 0000000..8c16ca2 --- /dev/null +++ b/examples/clients/typescript/sse-retry-test.ts @@ -0,0 +1,89 @@ +#!/usr/bin/env node + +/** + * SSE Retry Test Client + * + * Tests that the MCP client respects the SSE retry field when reconnecting. + * This client connects to a test server that closes the SSE stream mid-tool-call, + * then waits for the client to reconnect and sends the tool result. + */ + +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; + +async function main(): Promise { + const serverUrl = process.argv[2]; + + if (!serverUrl) { + console.error('Usage: sse-retry-test '); + process.exit(1); + } + + console.log(`Connecting to MCP server at: ${serverUrl}`); + console.log('This test validates SSE retry field compliance (SEP-1699)'); + + try { + const client = new Client( + { + name: 'sse-retry-test-client', + version: '1.0.0' + }, + { + capabilities: {} + } + ); + + const transport = new StreamableHTTPClientTransport(new URL(serverUrl), { + reconnectionOptions: { + initialReconnectionDelay: 1000, + maxReconnectionDelay: 10000, + reconnectionDelayGrowFactor: 1.5, + maxRetries: 3 + } + }); + + transport.onerror = (error) => { + console.log(`Transport error: ${error.message}`); + }; + + transport.onclose = () => { + console.log('Transport closed'); + }; + + console.log('Initiating connection...'); + await client.connect(transport); + console.log('Connected to MCP server'); + + console.log('Calling test_reconnection tool...'); + console.log( + 'Server will close SSE stream mid-call and send result after reconnection' + ); + + const result = await client.request( + { + method: 'tools/call', + params: { + name: 'test_reconnection', + arguments: {} + } + }, + CallToolResultSchema + ); + + console.log('Tool call completed:', JSON.stringify(result, null, 2)); + + await transport.close(); + console.log('Connection closed successfully'); + + process.exit(0); + } catch (error) { + console.error('Test failed:', error); + process.exit(1); + } +} + +main().catch((error) => { + console.error('Unhandled error:', error); + process.exit(1); +}); diff --git a/examples/servers/typescript/everything-server.ts b/examples/servers/typescript/everything-server.ts index 376434f..9dd382a 100644 --- a/examples/servers/typescript/everything-server.ts +++ b/examples/servers/typescript/everything-server.ts @@ -12,7 +12,12 @@ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import { + StreamableHTTPServerTransport, + EventStore, + EventId, + StreamId +} from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { ElicitResultSchema, ListToolsRequestSchema, @@ -33,6 +38,41 @@ const watchedResourceContent = 'Watched resource content'; const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; const servers: { [sessionId: string]: McpServer } = {}; +// In-memory event store for SEP-1699 resumability +const eventStoreData = new Map< + string, + { eventId: string; message: any; streamId: string } +>(); + +function createEventStore(): EventStore { + return { + async storeEvent(streamId: StreamId, message: any): Promise { + const eventId = `${streamId}::${Date.now()}_${randomUUID()}`; + eventStoreData.set(eventId, { eventId, message, streamId }); + return eventId; + }, + async replayEventsAfter( + lastEventId: EventId, + { send }: { send: (eventId: EventId, message: any) => Promise } + ): Promise { + const streamId = lastEventId.split('::')[0]; + const eventsToReplay: Array<[string, { message: any }]> = []; + for (const [eventId, data] of eventStoreData.entries()) { + if (data.streamId === streamId && eventId > lastEventId) { + eventsToReplay.push([eventId, data]); + } + } + eventsToReplay.sort(([a], [b]) => a.localeCompare(b)); + for (const [eventId, { message }] of eventsToReplay) { + if (Object.keys(message).length > 0) { + await send(eventId, message); + } + } + return streamId; + } + }; +} + // Sample base64 encoded 1x1 red PNG pixel for testing const TEST_IMAGE_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=='; @@ -309,6 +349,46 @@ function createMcpServer() { } ); + // SEP-1699: Reconnection test tool - closes SSE stream mid-call to test client reconnection + mcpServer.registerTool( + 'test_reconnection', + { + description: + 'Tests SSE stream disconnection and client reconnection (SEP-1699). Server will close the stream mid-call and send the result after client reconnects.', + inputSchema: {} + }, + async (_args, { sessionId, requestId }) => { + const sleep = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); + + console.log(`[${sessionId}] Starting test_reconnection tool...`); + + // Get the transport for this session + const transport = sessionId ? transports[sessionId] : undefined; + if (transport && requestId) { + // Close the SSE stream to trigger client reconnection + console.log( + `[${sessionId}] Closing SSE stream to trigger client polling...` + ); + transport.closeSSEStream(requestId); + } + + // Wait for client to reconnect (should respect retry field) + await sleep(100); + + console.log(`[${sessionId}] test_reconnection tool complete`); + + return { + content: [ + { + type: 'text', + text: 'Reconnection test completed successfully. If you received this, the client properly reconnected after stream closure.' + } + ] + }; + } + ); + // Sampling tool - requests LLM completion from client mcpServer.registerTool( 'test_sampling', @@ -1003,6 +1083,8 @@ app.post('/mcp', async (req, res) => { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), + eventStore: createEventStore(), + retryInterval: 5000, // 5 second retry interval for SEP-1699 onsessioninitialized: (newSessionId) => { transports[newSessionId] = transport; servers[newSessionId] = mcpServer; diff --git a/examples/servers/typescript/package-lock.json b/examples/servers/typescript/package-lock.json index b968361..4db8198 100644 --- a/examples/servers/typescript/package-lock.json +++ b/examples/servers/typescript/package-lock.json @@ -11,7 +11,8 @@ "@modelcontextprotocol/sdk": "^1.20.1", "@types/cors": "^2.8.19", "cors": "^2.8.5", - "express": "^5.1.0" + "express": "^5.1.0", + "zod-to-json-schema": "^3.25.0" }, "devDependencies": { "tsx": "^4.7.0", @@ -19,9 +20,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", - "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -36,9 +37,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/android-arm/-/android-arm-0.25.11.tgz", - "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -53,9 +54,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", - "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -70,9 +71,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/android-x64/-/android-x64-0.25.11.tgz", - "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -87,9 +88,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", - "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -104,9 +105,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", - "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -121,9 +122,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", - "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -138,9 +139,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", - "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -155,9 +156,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", - "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -172,9 +173,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", - "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -189,9 +190,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", - "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -206,9 +207,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", - "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -223,9 +224,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", - "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -240,9 +241,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", - "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -257,9 +258,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", - "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -274,9 +275,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", - "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -291,9 +292,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", - "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -308,9 +309,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", - "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -325,9 +326,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", - "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -342,9 +343,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", - "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -359,9 +360,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", - "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -376,9 +377,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", - "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], @@ -393,9 +394,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", - "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -410,9 +411,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", - "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -427,9 +428,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", - "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -444,9 +445,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", - "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -461,12 +462,13 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.20.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@modelcontextprotocol/sdk/-/sdk-1.20.1.tgz", - "integrity": "sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.22.0.tgz", + "integrity": "sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==", "license": "MIT", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", @@ -481,11 +483,19 @@ }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + } } }, "node_modules/@types/cors": { "version": "2.8.19", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@types/cors/-/cors-2.8.19.tgz", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "license": "MIT", "dependencies": { @@ -493,17 +503,17 @@ } }, "node_modules/@types/node": { - "version": "24.8.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@types/node/-/node-24.8.0.tgz", - "integrity": "sha512-5x08bUtU8hfboMTrJ7mEO4CpepS9yBwAqcL52y86SWNmbPX8LVbNs3EP4cNrIZgdjk2NAlP2ahNihozpoZIxSg==", + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.16.0" } }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/accepts/-/accepts-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { @@ -515,42 +525,41 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "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" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/async-generator-function": { - "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/async-generator-function/-/async-generator-function-1.0.0.tgz", - "integrity": "sha512-+NAXNqgCrB95ya4Sr66i1CL2hqLVckAk7xwRYWdcm39/ELQ6YNn1aw5r0bdQtqNZgQpEWzc5yc/igXc7aL5SLA==", + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, "node_modules/body-parser": { "version": "2.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/body-parser/-/body-parser-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "license": "MIT", "dependencies": { @@ -570,7 +579,7 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/bytes/-/bytes-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { @@ -579,7 +588,7 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { @@ -592,7 +601,7 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/call-bound/-/call-bound-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { @@ -607,20 +616,21 @@ } }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/content-type/-/content-type-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { @@ -629,7 +639,7 @@ }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/cookie/-/cookie-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { @@ -638,7 +648,7 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/cookie-signature/-/cookie-signature-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { @@ -647,7 +657,7 @@ }, "node_modules/cors": { "version": "2.8.5", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/cors/-/cors-2.8.5.tgz", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", "dependencies": { @@ -660,7 +670,7 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/cross-spawn/-/cross-spawn-7.0.6.tgz", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { @@ -674,7 +684,7 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/debug/-/debug-4.4.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { @@ -691,7 +701,7 @@ }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/depd/-/depd-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { @@ -700,7 +710,7 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/dunder-proto/-/dunder-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { @@ -714,13 +724,13 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ee-first/-/ee-first-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/encodeurl/-/encodeurl-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { @@ -729,7 +739,7 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/es-define-property/-/es-define-property-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { @@ -738,7 +748,7 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/es-errors/-/es-errors-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { @@ -747,7 +757,7 @@ }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { @@ -758,9 +768,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.11", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/esbuild/-/esbuild-0.25.11.tgz", - "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -771,43 +781,43 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.11", - "@esbuild/android-arm": "0.25.11", - "@esbuild/android-arm64": "0.25.11", - "@esbuild/android-x64": "0.25.11", - "@esbuild/darwin-arm64": "0.25.11", - "@esbuild/darwin-x64": "0.25.11", - "@esbuild/freebsd-arm64": "0.25.11", - "@esbuild/freebsd-x64": "0.25.11", - "@esbuild/linux-arm": "0.25.11", - "@esbuild/linux-arm64": "0.25.11", - "@esbuild/linux-ia32": "0.25.11", - "@esbuild/linux-loong64": "0.25.11", - "@esbuild/linux-mips64el": "0.25.11", - "@esbuild/linux-ppc64": "0.25.11", - "@esbuild/linux-riscv64": "0.25.11", - "@esbuild/linux-s390x": "0.25.11", - "@esbuild/linux-x64": "0.25.11", - "@esbuild/netbsd-arm64": "0.25.11", - "@esbuild/netbsd-x64": "0.25.11", - "@esbuild/openbsd-arm64": "0.25.11", - "@esbuild/openbsd-x64": "0.25.11", - "@esbuild/openharmony-arm64": "0.25.11", - "@esbuild/sunos-x64": "0.25.11", - "@esbuild/win32-arm64": "0.25.11", - "@esbuild/win32-ia32": "0.25.11", - "@esbuild/win32-x64": "0.25.11" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/escape-html/-/escape-html-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/etag/-/etag-1.8.1.tgz", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { @@ -816,7 +826,7 @@ }, "node_modules/eventsource": { "version": "3.0.7", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/eventsource/-/eventsource-3.0.7.tgz", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { @@ -828,7 +838,7 @@ }, "node_modules/eventsource-parser": { "version": "3.0.6", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "license": "MIT", "engines": { @@ -837,7 +847,7 @@ }, "node_modules/express": { "version": "5.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/express/-/express-5.1.0.tgz", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { @@ -879,7 +889,7 @@ }, "node_modules/express-rate-limit": { "version": "7.5.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "license": "MIT", "engines": { @@ -894,19 +904,29 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/finalhandler": { "version": "2.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/finalhandler/-/finalhandler-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { @@ -923,7 +943,7 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/forwarded/-/forwarded-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { @@ -932,7 +952,7 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/fresh/-/fresh-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { @@ -941,9 +961,10 @@ }, "node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/fsevents/-/fsevents-2.3.3.tgz", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -955,36 +976,24 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/function-bind/-/function-bind-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/get-intrinsic": { - "version": "1.3.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/get-intrinsic/-/get-intrinsic-1.3.1.tgz", - "integrity": "sha512-fk1ZVEeOX9hVZ6QzoBNEC55+Ucqg4sTVwrVuigZhuRPESVFpMyXnd3sbXvPOwp7Y9riVyANiqhEuRF0G1aVSeQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "async-function": "^1.0.0", - "async-generator-function": "^1.0.0", "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "generator-function": "^2.0.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", @@ -1000,7 +1009,7 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/get-proto/-/get-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { @@ -1012,9 +1021,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.12.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/get-tsconfig/-/get-tsconfig-4.12.0.tgz", - "integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1026,7 +1035,7 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/gopd/-/gopd-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { @@ -1038,8 +1047,9 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/has-symbols/-/has-symbols-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1049,7 +1059,7 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/hasown/-/hasown-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { @@ -1060,33 +1070,28 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/iconv-lite/-/iconv-lite-0.6.3.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { @@ -1098,13 +1103,13 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/inherits/-/inherits-2.0.4.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { @@ -1113,25 +1118,25 @@ }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/is-promise/-/is-promise-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { @@ -1140,7 +1145,7 @@ }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/media-typer/-/media-typer-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { @@ -1149,7 +1154,7 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { @@ -1161,7 +1166,7 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/mime-db/-/mime-db-1.54.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { @@ -1169,26 +1174,30 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ms/-/ms-2.1.3.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/negotiator/-/negotiator-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { @@ -1197,7 +1206,7 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { @@ -1206,7 +1215,7 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/object-inspect/-/object-inspect-1.13.4.tgz", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { @@ -1218,7 +1227,7 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/on-finished/-/on-finished-2.4.1.tgz", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { @@ -1230,7 +1239,7 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { @@ -1239,7 +1248,7 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/parseurl/-/parseurl-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { @@ -1248,7 +1257,7 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/path-key/-/path-key-3.1.1.tgz", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { @@ -1257,7 +1266,7 @@ }, "node_modules/path-to-regexp": { "version": "8.3.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { @@ -1266,9 +1275,9 @@ } }, "node_modules/pkce-challenge": { - "version": "5.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/pkce-challenge/-/pkce-challenge-5.0.0.tgz", - "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -1276,7 +1285,7 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/proxy-addr/-/proxy-addr-2.0.7.tgz", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { @@ -1287,18 +1296,9 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.14.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/qs/-/qs-6.14.0.tgz", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", "dependencies": { @@ -1313,7 +1313,7 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/range-parser/-/range-parser-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { @@ -1321,15 +1321,15 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" @@ -1337,7 +1337,7 @@ }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.7.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/iconv-lite/-/iconv-lite-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { @@ -1351,9 +1351,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", @@ -1363,7 +1372,7 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/router/-/router-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { @@ -1377,35 +1386,15 @@ "node": ">= 18" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/send": { "version": "1.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/send/-/send-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", "dependencies": { @@ -1427,7 +1416,7 @@ }, "node_modules/serve-static": { "version": "2.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/serve-static/-/serve-static-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", "dependencies": { @@ -1442,13 +1431,13 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/setprototypeof/-/setprototypeof-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/shebang-command/-/shebang-command-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { @@ -1460,7 +1449,7 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/shebang-regex/-/shebang-regex-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { @@ -1469,7 +1458,7 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/side-channel/-/side-channel-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { @@ -1488,7 +1477,7 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/side-channel-list/-/side-channel-list-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { @@ -1504,7 +1493,7 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/side-channel-map/-/side-channel-map-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { @@ -1522,7 +1511,7 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { @@ -1541,7 +1530,7 @@ }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/statuses/-/statuses-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { @@ -1550,7 +1539,7 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/toidentifier/-/toidentifier-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { @@ -1559,7 +1548,7 @@ }, "node_modules/tsx": { "version": "4.20.6", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/tsx/-/tsx-4.20.6.tgz", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", @@ -1579,7 +1568,7 @@ }, "node_modules/type-is": { "version": "2.0.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/type-is/-/type-is-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { @@ -1593,7 +1582,7 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/typescript/-/typescript-5.9.3.tgz", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", @@ -1606,31 +1595,23 @@ } }, "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/unpipe/-/unpipe-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/vary/-/vary-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { @@ -1639,7 +1620,7 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/which/-/which-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { @@ -1654,12 +1635,13 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/wrappy/1.0.2/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/zod/-/zod-3.25.76.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { @@ -1667,12 +1649,12 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } } } diff --git a/examples/servers/typescript/package.json b/examples/servers/typescript/package.json index 83e462e..5e12580 100644 --- a/examples/servers/typescript/package.json +++ b/examples/servers/typescript/package.json @@ -18,7 +18,8 @@ "@modelcontextprotocol/sdk": "^1.20.1", "@types/cors": "^2.8.19", "cors": "^2.8.5", - "express": "^5.1.0" + "express": "^5.1.0", + "zod-to-json-schema": "^3.25.0" }, "devDependencies": { "tsx": "^4.7.0", diff --git a/package-lock.json b/package-lock.json index 57b3450..fcfc981 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.23.0-beta.0", "commander": "^14.0.2", + "eventsource-parser": "^3.0.6", "express": "^5.1.0", "zod": "^3.25.76" }, diff --git a/package.json b/package.json index 4a56eb0..2dc81c3 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.23.0-beta.0", "commander": "^14.0.2", + "eventsource-parser": "^3.0.6", "express": "^5.1.0", "zod": "^3.25.76" } diff --git a/src/index.ts b/src/index.ts index a65e79f..6128a16 100644 --- a/src/index.ts +++ b/src/index.ts @@ -204,11 +204,14 @@ program 'Suite to run: "active" (default, excludes pending), "all", or "pending"', 'active' ) + .option('--verbose', 'Show verbose output (JSON instead of pretty print)') .action(async (options) => { try { // Validate options with Zod const validated = ServerOptionsSchema.parse(options); + const verbose = options.verbose ?? false; + // If a single scenario is specified, run just that one if (validated.scenario) { const result = await runServerConformanceTest( @@ -218,7 +221,8 @@ program const { failed } = printServerResults( result.checks, - result.scenarioDescription + result.scenarioDescription, + verbose ); process.exit(failed > 0 ? 1 : 0); } else { diff --git a/src/runner/server.ts b/src/runner/server.ts index 46be558..18c8254 100644 --- a/src/runner/server.ts +++ b/src/runner/server.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'fs'; import path from 'path'; import { ConformanceCheck } from '../types'; import { getClientScenario } from '../scenarios'; -import { ensureResultsDir, createResultDir } from './utils'; +import { ensureResultsDir, createResultDir, formatPrettyChecks } from './utils'; /** * Format markdown-style text for terminal output using ANSI codes @@ -54,7 +54,8 @@ export async function runServerConformanceTest( export function printServerResults( checks: ConformanceCheck[], - scenarioDescription: string + scenarioDescription: string, + verbose: boolean = false ): { passed: number; failed: number; @@ -68,7 +69,11 @@ export function printServerResults( const failed = checks.filter((c) => c.status === 'FAILURE').length; const warnings = checks.filter((c) => c.status === 'WARNING').length; - console.log(`Checks:\n${JSON.stringify(checks, null, 2)}`); + if (verbose) { + console.log(JSON.stringify(checks, null, 2)); + } else { + console.log(`Checks:\n${formatPrettyChecks(checks)}`); + } console.log(`\nTest Results:`); console.log( diff --git a/src/scenarios/client/sse-retry.ts b/src/scenarios/client/sse-retry.ts new file mode 100644 index 0000000..6e44a06 --- /dev/null +++ b/src/scenarios/client/sse-retry.ts @@ -0,0 +1,555 @@ +/** + * SSE Retry conformance test scenarios for MCP clients (SEP-1699) + * + * Tests that clients properly respect the SSE retry field by: + * - Waiting the specified milliseconds before reconnecting + * - Sending Last-Event-ID header on reconnection + * - Treating graceful stream closure as reconnectable + */ + +import http from 'http'; +import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types.js'; + +export class SSERetryScenario implements Scenario { + name = 'sse-retry'; + description = + 'Tests that client respects SSE retry field timing and reconnects properly (SEP-1699)'; + + private server: http.Server | null = null; + private checks: ConformanceCheck[] = []; + private port: number = 0; + + // Timing tracking + private toolStreamCloseTime: number | null = null; + private getReconnectionTime: number | null = null; + private getConnectionCount: number = 0; + private lastEventIds: (string | undefined)[] = []; + private retryValue: number = 500; // 500ms + private eventIdCounter: number = 0; + private sessionId: string = `session-${Date.now()}`; + + // Pending tool call to respond to after reconnection + private pendingToolCallId: number | string | null = null; + private getResponseStream: http.ServerResponse | null = null; + + // Tolerances for timing validation + private readonly EARLY_TOLERANCE = 50; // Allow 50ms early for scheduler variance + private readonly LATE_TOLERANCE = 200; // Allow 200ms late for network/event loop + private readonly VERY_LATE_MULTIPLIER = 2; // If >2x retry value, client is likely ignoring it + + async start(): Promise { + return new Promise((resolve, reject) => { + this.server = http.createServer((req, res) => { + this.handleRequest(req, res); + }); + + this.server.on('error', reject); + + this.server.listen(0, () => { + const address = this.server!.address(); + if (address && typeof address === 'object') { + this.port = address.port; + resolve({ + serverUrl: `http://localhost:${this.port}` + }); + } else { + reject(new Error('Failed to get server address')); + } + }); + }); + } + + async stop(): Promise { + return new Promise((resolve, reject) => { + if (this.server) { + this.server.close((err) => { + if (err) { + reject(err); + } else { + this.server = null; + resolve(); + } + }); + } else { + resolve(); + } + }); + } + + getChecks(): ConformanceCheck[] { + // Generate checks based on observed behavior + this.generateChecks(); + return this.checks; + } + + private handleRequest( + req: http.IncomingMessage, + res: http.ServerResponse + ): void { + if (req.method === 'GET') { + // Track GET reconnection timing and Last-Event-ID + this.getConnectionCount++; + this.getReconnectionTime = performance.now(); + + const lastEventId = req.headers['last-event-id'] as string | undefined; + const description = lastEventId + ? `Received GET request for ${req.url} (Last-Event-ID: ${lastEventId})` + : `Received GET request for ${req.url}`; + this.checks.push({ + id: 'incoming-request', + name: 'IncomingRequest', + description, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + method: 'GET', + url: req.url, + headers: req.headers, + connectionCount: this.getConnectionCount + } + }); + + if (lastEventId) { + this.lastEventIds.push(lastEventId); + } + + // Handle GET SSE stream request (reconnection) + this.handleGetSSEStream(req, res); + } else if (req.method === 'POST') { + // Handle POST JSON-RPC requests + this.handlePostRequest(req, res); + } else { + res.writeHead(405); + res.end('Method Not Allowed'); + } + } + + private handleGetSSEStream( + _req: http.IncomingMessage, + res: http.ServerResponse + ): void { + // Set SSE headers + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'mcp-session-id': this.sessionId + }); + + // Generate event ID + this.eventIdCounter++; + const eventId = `event-${this.eventIdCounter}`; + + // Send priming event with ID and retry field + const primingContent = `id: ${eventId}\nretry: ${this.retryValue}\ndata: \n\n`; + res.write(primingContent); + + this.checks.push({ + id: 'outgoing-sse-event', + name: 'OutgoingSseEvent', + description: `Sent SSE priming event on GET stream (id: ${eventId}, retry: ${this.retryValue}ms)`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId, + retryMs: this.retryValue, + eventType: 'priming', + raw: primingContent + } + }); + + // Store the GET stream to send pending tool response + this.getResponseStream = res; + + // If we have a pending tool call, send the response now + if (this.pendingToolCallId !== null) { + const toolResponse = { + jsonrpc: '2.0', + id: this.pendingToolCallId, + result: { + content: [ + { + type: 'text', + text: 'Reconnection test completed successfully' + } + ] + } + }; + + const responseEventId = `event-${++this.eventIdCounter}`; + const responseContent = `event: message\nid: ${responseEventId}\ndata: ${JSON.stringify(toolResponse)}\n\n`; + res.write(responseContent); + + this.checks.push({ + id: 'outgoing-sse-event', + name: 'OutgoingSseEvent', + description: `Sent tool response on GET stream after reconnection (id: ${responseEventId})`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId: responseEventId, + eventType: 'message', + jsonrpcId: this.pendingToolCallId, + body: toolResponse, + raw: responseContent + } + }); + + this.pendingToolCallId = null; + } + } + + private handlePostRequest( + req: http.IncomingMessage, + res: http.ServerResponse + ): void { + let body = ''; + + req.on('data', (chunk) => { + body += chunk.toString(); + }); + + req.on('end', () => { + try { + const request = JSON.parse(body); + + this.checks.push({ + id: 'incoming-request', + name: 'IncomingRequest', + description: `Received POST request for ${req.url} (method: ${request.method})`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + method: 'POST', + url: req.url, + jsonrpcMethod: request.method, + jsonrpcId: request.id + } + }); + + if (request.method === 'initialize') { + this.handleInitialize(req, res, request); + } else if (request.method === 'tools/list') { + this.handleToolsList(res, request); + } else if (request.method === 'tools/call') { + this.handleToolsCall(res, request); + } else if (request.id === undefined) { + // Notifications (no id) - return 202 Accepted + res.writeHead(202); + res.end(); + } else { + // For other requests, send a simple JSON response + res.writeHead(200, { + 'Content-Type': 'application/json', + 'mcp-session-id': this.sessionId + }); + res.end( + JSON.stringify({ + jsonrpc: '2.0', + id: request.id, + result: {} + }) + ); + } + } catch (error) { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + jsonrpc: '2.0', + error: { + code: -32700, + message: `Parse error: ${error}` + } + }) + ); + } + }); + } + + private handleInitialize( + _req: http.IncomingMessage, + res: http.ServerResponse, + request: any + ): void { + res.writeHead(200, { + 'Content-Type': 'application/json', + 'mcp-session-id': this.sessionId + }); + + const response = { + jsonrpc: '2.0', + id: request.id, + result: { + protocolVersion: '2025-03-26', + serverInfo: { + name: 'sse-retry-test-server', + version: '1.0.0' + }, + capabilities: { + tools: {} + } + } + }; + + res.end(JSON.stringify(response)); + + this.checks.push({ + id: 'outgoing-response', + name: 'OutgoingResponse', + description: `Sent initialize response`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + jsonrpcId: request.id, + body: response + } + }); + } + + private handleToolsList(res: http.ServerResponse, request: any): void { + res.writeHead(200, { + 'Content-Type': 'application/json', + 'mcp-session-id': this.sessionId + }); + + const response = { + jsonrpc: '2.0', + id: request.id, + result: { + tools: [ + { + name: 'test_reconnection', + description: + 'A tool that triggers SSE stream closure to test client reconnection behavior', + inputSchema: { + type: 'object', + properties: {}, + required: [] + } + } + ] + } + }; + + res.end(JSON.stringify(response)); + + this.checks.push({ + id: 'outgoing-response', + name: 'OutgoingResponse', + description: `Sent tools/list response`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + jsonrpcId: request.id, + body: response + } + }); + } + + private handleToolsCall(res: http.ServerResponse, request: any): void { + // Store the request ID so we can respond after reconnection + this.pendingToolCallId = request.id; + + // Start SSE stream + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'mcp-session-id': this.sessionId + }); + + // Send priming event with retry field + this.eventIdCounter++; + const primingEventId = `event-${this.eventIdCounter}`; + const primingContent = `id: ${primingEventId}\nretry: ${this.retryValue}\ndata: \n\n`; + res.write(primingContent); + + this.checks.push({ + id: 'outgoing-sse-event', + name: 'OutgoingSseEvent', + description: `Sent SSE priming event for tools/call (id: ${primingEventId}, retry: ${this.retryValue}ms)`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId: primingEventId, + retryMs: this.retryValue, + eventType: 'priming', + raw: primingContent + } + }); + + // Close the stream after a short delay to trigger reconnection + setTimeout(() => { + this.toolStreamCloseTime = performance.now(); + this.checks.push({ + id: 'outgoing-stream-close', + name: 'OutgoingStreamClose', + description: + 'Closed tools/call SSE stream to trigger client reconnection', + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + retryMs: this.retryValue, + pendingToolCallId: this.pendingToolCallId + } + }); + res.end(); + }, 50); + } + + private generateChecks(): void { + // Check 1: Client should have reconnected via GET after tool call stream close + if (this.getConnectionCount < 1) { + this.checks.push({ + id: 'client-sse-graceful-reconnect', + name: 'ClientGracefulReconnect', + description: + 'Client reconnects via GET after SSE stream is closed gracefully', + status: 'FAILURE', + timestamp: new Date().toISOString(), + errorMessage: `Client did not attempt GET reconnection after stream closure. Client should treat graceful stream close as reconnectable.`, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + getConnectionCount: this.getConnectionCount, + toolStreamCloseTime: this.toolStreamCloseTime, + retryValue: this.retryValue + } + }); + return; + } + + // Client did reconnect - SUCCESS for graceful reconnection + this.checks.push({ + id: 'client-sse-graceful-reconnect', + name: 'ClientGracefulReconnect', + description: + 'Client reconnects via GET after SSE stream is closed gracefully', + status: 'SUCCESS', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + getConnectionCount: this.getConnectionCount + } + }); + + // Check 2: Client MUST respect retry field timing + if ( + this.toolStreamCloseTime !== null && + this.getReconnectionTime !== null + ) { + const actualDelay = this.getReconnectionTime - this.toolStreamCloseTime; + const minExpected = this.retryValue - this.EARLY_TOLERANCE; + const maxExpected = this.retryValue + this.LATE_TOLERANCE; + + const tooEarly = actualDelay < minExpected; + const slightlyLate = actualDelay > maxExpected; + const veryLate = + actualDelay > this.retryValue * this.VERY_LATE_MULTIPLIER; + const withinTolerance = !tooEarly && !slightlyLate; + + let status: 'SUCCESS' | 'FAILURE' | 'WARNING' = 'SUCCESS'; + let errorMessage: string | undefined; + + if (tooEarly) { + // Client reconnected too soon - MUST violation + status = 'FAILURE'; + errorMessage = `Client reconnected too early (${actualDelay.toFixed(0)}ms instead of ${this.retryValue}ms). Client MUST respect the retry field and wait the specified time.`; + } else if (veryLate) { + // Client reconnected way too late - likely ignoring retry field entirely + status = 'FAILURE'; + errorMessage = `Client reconnected very late (${actualDelay.toFixed(0)}ms instead of ${this.retryValue}ms). Client appears to be ignoring the retry field and using its own backoff strategy.`; + } else if (slightlyLate) { + // Client reconnected slightly late - not a spec violation but suspicious + status = 'WARNING'; + errorMessage = `Client reconnected slightly late (${actualDelay.toFixed(0)}ms instead of ${this.retryValue}ms). This is acceptable but may indicate network delays.`; + } + + this.checks.push({ + id: 'client-sse-retry-timing', + name: 'ClientRespectsRetryField', + description: + 'Client MUST respect the retry field, waiting the given number of milliseconds before attempting to reconnect', + status, + timestamp: new Date().toISOString(), + errorMessage, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + expectedRetryMs: this.retryValue, + actualDelayMs: Math.round(actualDelay), + minAcceptableMs: minExpected, + maxAcceptableMs: maxExpected, + veryLateThresholdMs: this.retryValue * this.VERY_LATE_MULTIPLIER, + earlyToleranceMs: this.EARLY_TOLERANCE, + lateToleranceMs: this.LATE_TOLERANCE, + withinTolerance, + tooEarly, + slightlyLate, + veryLate, + getConnectionCount: this.getConnectionCount + } + }); + } else { + this.checks.push({ + id: 'client-sse-retry-timing', + name: 'ClientRespectsRetryField', + description: 'Client MUST respect the retry field timing', + status: 'WARNING', + timestamp: new Date().toISOString(), + errorMessage: + 'Could not measure timing - tool stream close time or GET reconnection time not recorded', + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + toolStreamCloseTime: this.toolStreamCloseTime, + getReconnectionTime: this.getReconnectionTime + } + }); + } + + // Check 3: Client SHOULD send Last-Event-ID header on reconnection + const hasLastEventId = + this.lastEventIds.length > 0 && this.lastEventIds[0] !== undefined; + + this.checks.push({ + id: 'client-sse-last-event-id', + name: 'ClientSendsLastEventId', + description: + 'Client SHOULD send Last-Event-ID header on reconnection for resumability', + status: hasLastEventId ? 'SUCCESS' : 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + hasLastEventId, + lastEventIds: this.lastEventIds, + getConnectionCount: this.getConnectionCount + }, + errorMessage: !hasLastEventId + ? 'Client did not send Last-Event-ID header on reconnection. This is a SHOULD requirement for resumability.' + : undefined + }); + } +} diff --git a/src/scenarios/index.ts b/src/scenarios/index.ts index f0d1e78..de76cd6 100644 --- a/src/scenarios/index.ts +++ b/src/scenarios/index.ts @@ -2,6 +2,7 @@ import { Scenario, ClientScenario } from '../types'; import { InitializeScenario } from './client/initialize'; import { ToolsCallScenario } from './client/tools_call'; import { ElicitationClientDefaultsScenario } from './client/elicitation-defaults'; +import { SSERetryScenario } from './client/sse-retry'; // Import all new server test scenarios import { ServerInitializeScenario } from './server/lifecycle'; @@ -29,6 +30,8 @@ import { JsonSchema2020_12Scenario } from './server/json-schema-2020-12'; import { ElicitationDefaultsScenario } from './server/elicitation-defaults'; import { ElicitationEnumsScenario } from './server/elicitation-enums'; +import { ServerSSEPollingScenario } from './server/sse-polling'; +import { ServerSSEMultipleStreamsScenario } from './server/sse-multiple-streams'; import { ResourcesListScenario, @@ -63,7 +66,11 @@ const pendingClientScenariosList: ClientScenario[] = [ // On hold until elicitation schema types are fixed // https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1863 new ToolsCallElicitationScenario(), - new ElicitationDefaultsScenario() + new ElicitationDefaultsScenario(), + + // On hold until server-side SSE improvements are made + // https://github.com/modelcontextprotocol/typescript-sdk/pull/1129 + new ServerSSEPollingScenario() ]; // All client scenarios @@ -94,6 +101,10 @@ const allClientScenariosList: ClientScenario[] = [ // Elicitation scenarios (SEP-1034) new ElicitationDefaultsScenario(), + // SSE Polling scenarios (SEP-1699) + new ServerSSEPollingScenario(), + new ServerSSEMultipleStreamsScenario(), + // Elicitation scenarios (SEP-1330) - pending new ElicitationEnumsScenario(), @@ -132,6 +143,7 @@ const scenariosList: Scenario[] = [ new InitializeScenario(), new ToolsCallScenario(), new ElicitationClientDefaultsScenario(), + new SSERetryScenario(), ...authScenariosList ]; diff --git a/src/scenarios/server/all-scenarios.test.ts b/src/scenarios/server/all-scenarios.test.ts index 9f9a7e9..bbff157 100644 --- a/src/scenarios/server/all-scenarios.test.ts +++ b/src/scenarios/server/all-scenarios.test.ts @@ -125,9 +125,9 @@ describe('Server Scenarios', () => { throw new Error(`Scenario failed with checks:\n ${failureMessages}`); } - // All checks should be SUCCESS - const successes = checks.filter((c) => c.status === 'SUCCESS'); - expect(successes.length).toBe(checks.length); + // All checks should be non-FAILURE (SUCCESS, WARNING, or INFO are acceptable) + const nonFailures = checks.filter((c) => c.status !== 'FAILURE'); + expect(nonFailures.length).toBe(checks.length); }, 10000); // 10 second timeout per scenario } }); diff --git a/src/scenarios/server/sse-multiple-streams.ts b/src/scenarios/server/sse-multiple-streams.ts new file mode 100644 index 0000000..7cda3f2 --- /dev/null +++ b/src/scenarios/server/sse-multiple-streams.ts @@ -0,0 +1,268 @@ +/** + * SSE Multiple Streams conformance test scenarios for MCP servers (SEP-1699) + * + * Tests that servers properly support multiple concurrent SSE streams: + * - Accepting multiple POST requests that return SSE streams simultaneously + * - Each POST request gets its own stream with unique stream ID + * + * Note: The standalone GET stream (without Last-Event-ID) is limited to one per session. + * Multiple concurrent streams are achieved via POST requests, each getting their own stream. + */ + +import { ClientScenario, ConformanceCheck } from '../../types.js'; +import { EventSourceParserStream } from 'eventsource-parser/stream'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; + +export class ServerSSEMultipleStreamsScenario implements ClientScenario { + name = 'server-sse-multiple-streams'; + description = + 'Test server supports multiple concurrent POST SSE streams (SEP-1699)'; + + async run(serverUrl: string): Promise { + const checks: ConformanceCheck[] = []; + + let sessionId: string | undefined; + let client: Client | undefined; + let transport: StreamableHTTPClientTransport | undefined; + + try { + // Step 1: Initialize session with the server + client = new Client( + { + name: 'conformance-test-client', + version: '1.0.0' + }, + { + capabilities: { + sampling: {}, + elicitation: {} + } + } + ); + + transport = new StreamableHTTPClientTransport(new URL(serverUrl)); + await client.connect(transport); + + // Extract session ID from transport + sessionId = (transport as unknown as { sessionId?: string }).sessionId; + + if (!sessionId) { + checks.push({ + id: 'server-sse-multiple-streams-session', + name: 'ServerSSEMultipleStreamsSession', + description: 'Server provides session ID for multiple streams test', + status: 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + message: + 'Server did not provide session ID - multiple streams test may not work correctly' + } + }); + return checks; + } + + // Step 2: Open multiple POST SSE streams concurrently + // Each POST request gets its own stream with unique streamId + // Spec says: "The client MAY remain connected to multiple SSE streams simultaneously" + const streamResponses: Response[] = []; + const numStreams = 3; + + // Launch all POST requests concurrently + const postPromises = []; + for (let i = 0; i < numStreams; i++) { + const promise = fetch(serverUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'text/event-stream, application/json', + 'mcp-session-id': sessionId, + 'mcp-protocol-version': '2025-03-26' + }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1000 + i, // Different request IDs for each stream + method: 'tools/list', + params: {} + }) + }); + postPromises.push(promise); + } + + // Wait for all responses + const responses = await Promise.all(postPromises); + streamResponses.push(...responses); + + // Check that all streams were accepted (HTTP 200) + const allAccepted = streamResponses.every((r) => r.ok); + const statuses = streamResponses.map((r) => r.status); + const contentTypes = streamResponses.map((r) => + r.headers.get('content-type') + ); + + // Count how many returned SSE streams vs JSON + const sseStreams = contentTypes.filter((ct) => + ct?.includes('text/event-stream') + ).length; + + checks.push({ + id: 'server-accepts-multiple-post-streams', + name: 'ServerAcceptsMultiplePostStreams', + description: + 'Server allows multiple concurrent POST requests (each may return SSE or JSON)', + status: allAccepted ? 'SUCCESS' : 'FAILURE', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + numStreamsAttempted: numStreams, + numStreamsAccepted: statuses.filter((s) => s === 200).length, + numSseStreams: sseStreams, + statuses, + contentTypes + }, + errorMessage: !allAccepted + ? `Server rejected some requests. Statuses: ${statuses.join(', ')}` + : undefined + }); + + // Step 3: Verify SSE streams are functional by reading events + // Only test streams that returned SSE content-type + const eventResults = await Promise.all( + streamResponses.map(async (response, index) => { + const contentType = response.headers.get('content-type'); + + // Skip non-SSE responses (JSON responses are also valid) + if (!contentType?.includes('text/event-stream')) { + return { index, type: 'json', skipped: true }; + } + + if (!response.ok || !response.body) { + return { index, type: 'sse', error: 'Stream not available' }; + } + + try { + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new EventSourceParserStream()) + .getReader(); + + // Wait for one event with timeout + const timeoutPromise = new Promise((resolve) => + setTimeout(() => resolve(null), 2000) + ); + + const eventPromise = reader.read().then(({ value }) => value); + + const event = await Promise.race([eventPromise, timeoutPromise]); + + // Cancel reader + await reader.cancel(); + + return { index, type: 'sse', event }; + } catch (error) { + return { + index, + type: 'sse', + error: error instanceof Error ? error.message : String(error) + }; + } + }) + ); + + // Count functional SSE streams (received event or timed out waiting - both are valid) + const sseResults = eventResults.filter((r) => r.type === 'sse'); + const functionalSseStreams = sseResults.filter( + (r) => !('error' in r) + ).length; + + // If server returned SSE streams, they should be functional + if (sseStreams > 0) { + checks.push({ + id: 'server-sse-streams-functional', + name: 'ServerSSEStreamsFunctional', + description: 'Multiple POST SSE streams should be functional', + status: + functionalSseStreams === sseStreams + ? 'SUCCESS' + : functionalSseStreams > 0 + ? 'WARNING' + : 'FAILURE', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + numSseStreams: sseStreams, + functionalSseStreams, + results: eventResults + }, + errorMessage: + functionalSseStreams < sseStreams + ? `Only ${functionalSseStreams}/${sseStreams} SSE streams were functional` + : undefined + }); + } else { + // Server returned JSON for all requests - this is valid but worth noting + checks.push({ + id: 'server-sse-streams-functional', + name: 'ServerSSEStreamsFunctional', + description: 'Server returned JSON responses (SSE streams optional)', + status: 'INFO', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + numSseStreams: 0, + message: + 'Server returned JSON for all requests - SSE streaming is optional', + results: eventResults + } + }); + } + } catch (error) { + checks.push({ + id: 'server-sse-multiple-streams-error', + name: 'ServerSSEMultipleStreamsTest', + description: 'Test server multiple SSE streams behavior', + status: 'FAILURE', + timestamp: new Date().toISOString(), + errorMessage: `Error: ${error instanceof Error ? error.message : String(error)}`, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ] + }); + } finally { + // Clean up + if (client) { + try { + await client.close(); + } catch { + // Ignore cleanup errors + } + } + } + + return checks; + } +} diff --git a/src/scenarios/server/sse-polling.ts b/src/scenarios/server/sse-polling.ts new file mode 100644 index 0000000..5a3a240 --- /dev/null +++ b/src/scenarios/server/sse-polling.ts @@ -0,0 +1,601 @@ +/** + * SSE Polling conformance test scenarios for MCP servers (SEP-1699) + * + * Tests that servers properly implement SSE polling behavior including: + * - Sending priming events with event ID and empty data on POST SSE streams + * - Sending retry field in priming events when configured + * - Closing SSE stream mid-operation and resuming after client reconnects + * - Replaying events when client reconnects with Last-Event-ID + */ + +import { ClientScenario, ConformanceCheck } from '../../types.js'; +import { EventSourceParserStream } from 'eventsource-parser/stream'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; + +function createLoggingFetch(checks: ConformanceCheck[]) { + return async (url: string, options: RequestInit): Promise => { + const method = options.method || 'GET'; + let description = `Sending ${method} request`; + if (options.body) { + try { + const body = JSON.parse(options.body as string); + if (body.method) { + description = `Sending ${method} ${body.method}`; + } + } catch { + // Not JSON + } + } + + checks.push({ + id: 'outgoing-request', + name: 'OutgoingRequest', + description, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + method, + url, + headers: options.headers, + body: options.body ? JSON.parse(options.body as string) : undefined + } + }); + + const response = await fetch(url, options); + + const responseHeaders: Record = {}; + response.headers.forEach((value, key) => { + responseHeaders[key] = value; + }); + + checks.push({ + id: 'incoming-response', + name: 'IncomingResponse', + description: `Received ${response.status} response for ${method}`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + statusCode: response.status, + headers: responseHeaders + } + }); + + return response; + }; +} + +export class ServerSSEPollingScenario implements ClientScenario { + name = 'server-sse-polling'; + description = + 'Test server SSE polling via test_reconnection tool that closes stream mid-call (SEP-1699)'; + + async run(serverUrl: string): Promise { + const checks: ConformanceCheck[] = []; + + let sessionId: string | undefined; + let client: Client | undefined; + let transport: StreamableHTTPClientTransport | undefined; + + try { + // Step 1: Initialize session with the server + client = new Client( + { + name: 'conformance-test-client', + version: '1.0.0' + }, + { + capabilities: { + sampling: {}, + elicitation: {} + } + } + ); + + transport = new StreamableHTTPClientTransport(new URL(serverUrl)); + await client.connect(transport); + + // Extract session ID from transport (accessing internal state) + sessionId = (transport as unknown as { sessionId?: string }).sessionId; + + if (!sessionId) { + checks.push({ + id: 'server-sse-polling-session', + name: 'ServerSSEPollingSession', + description: 'Server provides session ID for SSE polling tests', + status: 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + message: + 'Server did not provide session ID - SSE polling tests may not work correctly' + } + }); + } + + // Step 2: Call test_reconnection tool via raw fetch to observe SSE behavior + // This tool should close the stream mid-call, requiring reconnection + const loggingFetch = createLoggingFetch(checks); + + const postResponse = await loggingFetch(serverUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'text/event-stream, application/json', + ...(sessionId && { 'mcp-session-id': sessionId }), + 'mcp-protocol-version': '2025-03-26' + }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'tools/call', + params: { + name: 'test_reconnection', + arguments: {} + } + }) + }); + + if (!postResponse.ok) { + // Check if tool doesn't exist (method not found or similar) + if (postResponse.status === 400 || postResponse.status === 404) { + checks.push({ + id: 'server-sse-test-reconnection-tool', + name: 'ServerTestReconnectionTool', + description: + 'Server implements test_reconnection tool for SSE polling tests', + status: 'WARNING', + timestamp: new Date().toISOString(), + errorMessage: `Server does not implement test_reconnection tool (HTTP ${postResponse.status}). This tool is recommended for testing SSE polling behavior.`, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ] + }); + return checks; + } + + checks.push({ + id: 'server-sse-post-request', + name: 'ServerSSEPostRequest', + description: 'Server accepts POST request with SSE stream response', + status: 'FAILURE', + timestamp: new Date().toISOString(), + errorMessage: `Server returned HTTP ${postResponse.status}`, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ] + }); + return checks; + } + + // Check if server returned SSE stream + const contentType = postResponse.headers.get('content-type'); + if (!contentType?.includes('text/event-stream')) { + checks.push({ + id: 'server-sse-content-type', + name: 'ServerSSEContentType', + description: 'Server returns text/event-stream for POST request', + status: 'INFO', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + contentType, + message: + 'Server returned JSON instead of SSE stream - priming event tests not applicable' + } + }); + return checks; + } + + // Step 3: Parse SSE stream for priming event and tool response + let hasEventId = false; + let hasPrimingEvent = false; + let primingEventIsFirst = false; + let hasRetryField = false; + let retryValue: number | undefined; + let lastEventId: string | undefined; + let eventCount = 0; + let receivedToolResponse = false; + + if (!postResponse.body) { + checks.push({ + id: 'server-sse-polling-stream', + name: 'ServerSSEPollingStream', + description: 'Server provides SSE response body', + status: 'FAILURE', + timestamp: new Date().toISOString(), + errorMessage: 'Response body is null', + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ] + }); + return checks; + } + + const reader = postResponse.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough( + new EventSourceParserStream({ + onRetry: (retryMs: number) => { + hasRetryField = true; + retryValue = retryMs; + } + }) + ) + .getReader(); + + // Read events with timeout - expect stream to close before we get the response + const timeout = setTimeout(() => { + reader.cancel(); + }, 10000); + + try { + while (true) { + const { value: event, done } = await reader.read(); + + if (done) { + break; + } + + eventCount++; + + // Track the last event ID for reconnection + if (event.id) { + hasEventId = true; + lastEventId = event.id; + + // Check if this is a priming event (empty or minimal data) + const isPriming = + event.data === '' || + event.data === '{}' || + event.data.trim() === ''; + if (isPriming) { + hasPrimingEvent = true; + // Check if priming event is the first event + if (eventCount === 1) { + primingEventIsFirst = true; + } + } + + // Log the SSE event + checks.push({ + id: 'incoming-sse-event', + name: 'IncomingSseEvent', + description: isPriming + ? `Received SSE priming event (id: ${event.id})` + : `Received SSE event (id: ${event.id})`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId: event.id, + eventType: event.event || 'message', + isPriming, + hasRetryField, + retryValue, + data: event.data + } + }); + } + + // Check if this is the tool response + if (event.data) { + try { + const parsed = JSON.parse(event.data); + if (parsed.id === 1 && parsed.result) { + receivedToolResponse = true; + const isError = parsed.result?.isError === true; + checks.push({ + id: 'incoming-sse-event', + name: 'IncomingSseEvent', + description: `Received tool response on POST stream`, + status: isError ? 'FAILURE' : 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId: event.id, + body: parsed + }, + ...(isError && { errorMessage: `Tool call failed` }) + }); + } + } catch { + // Not JSON, ignore + } + } + } + } finally { + clearTimeout(timeout); + } + + // Log stream closure + checks.push({ + id: 'stream-closed', + name: 'StreamClosed', + description: `POST SSE stream closed after ${eventCount} event(s)`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventCount, + lastEventId, + receivedToolResponse + } + }); + + // Check 1: Server SHOULD send priming event with ID on POST SSE stream + let primingStatus: 'SUCCESS' | 'WARNING' = 'SUCCESS'; + let primingErrorMessage: string | undefined; + + if (!hasPrimingEvent) { + primingStatus = 'WARNING'; + primingErrorMessage = + 'Server did not send priming event with id and empty data on POST SSE stream. This is recommended for resumability.'; + } else if (!primingEventIsFirst) { + primingStatus = 'WARNING'; + primingErrorMessage = + 'Priming event was not sent first. It should be sent immediately when the SSE stream is established.'; + } + + checks.push({ + id: 'server-sse-priming-event', + name: 'ServerSendsPrimingEvent', + description: + 'Server SHOULD send priming event with id and empty data on POST SSE streams', + status: primingStatus, + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + hasPrimingEvent, + primingEventIsFirst, + hasEventId, + lastEventId, + eventCount + }, + errorMessage: primingErrorMessage + }); + + // Check 2: Server SHOULD send retry field in priming event + checks.push({ + id: 'server-sse-retry-field', + name: 'ServerSendsRetryField', + description: + 'Server SHOULD send retry field to control client reconnection timing', + status: hasRetryField ? 'SUCCESS' : 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + hasRetryField, + retryValue + }, + errorMessage: !hasRetryField + ? 'Server did not send retry field. This is recommended for controlling client reconnection timing.' + : undefined + }); + + // Step 4: If tool response wasn't received, reconnect with Last-Event-ID + if (!receivedToolResponse && lastEventId && sessionId) { + // Make a GET request with Last-Event-ID to get the tool response + const getResponse = await loggingFetch(serverUrl, { + method: 'GET', + headers: { + Accept: 'text/event-stream', + 'mcp-session-id': sessionId, + 'mcp-protocol-version': '2025-03-26', + 'last-event-id': lastEventId + } + }); + + if (getResponse.ok && getResponse.body) { + const reconnectReader = getResponse.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new EventSourceParserStream()) + .getReader(); + + const reconnectTimeout = setTimeout(() => { + reconnectReader.cancel(); + }, 5000); + + try { + while (true) { + const { value: event, done } = await reconnectReader.read(); + if (done) break; + + // Log each event received on GET stream + checks.push({ + id: 'incoming-sse-event', + name: 'IncomingSseEvent', + description: `Received SSE event on GET reconnection stream (id: ${event.id || 'none'})`, + status: 'INFO', + timestamp: new Date().toISOString(), + details: { + eventId: event.id, + eventType: event.event || 'message', + data: event.data + } + }); + + // Check if this is the tool response + if (event.data) { + try { + const parsed = JSON.parse(event.data); + if (parsed.id === 1 && parsed.result) { + receivedToolResponse = true; + break; + } + } catch { + // Not JSON, ignore + } + } + } + } finally { + clearTimeout(reconnectTimeout); + } + + checks.push({ + id: 'server-sse-disconnect-resume', + name: 'ServerDisconnectResume', + description: + 'Server closes SSE stream mid-call and resumes after client reconnects with Last-Event-ID', + status: receivedToolResponse ? 'SUCCESS' : 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + lastEventIdUsed: lastEventId, + receivedToolResponse, + message: receivedToolResponse + ? 'Successfully received tool response after reconnection' + : 'Tool response not received after reconnection' + }, + errorMessage: !receivedToolResponse + ? 'Server did not send tool response after client reconnected with Last-Event-ID' + : undefined + }); + } else { + // Check if server doesn't support standalone GET streams + if (getResponse.status === 405) { + checks.push({ + id: 'server-sse-disconnect-resume', + name: 'ServerDisconnectResume', + description: + 'Server supports GET reconnection with Last-Event-ID', + status: 'INFO', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + statusCode: getResponse.status, + message: + 'Server does not support standalone GET SSE endpoint (405 Method Not Allowed)' + } + }); + } else { + checks.push({ + id: 'server-sse-disconnect-resume', + name: 'ServerDisconnectResume', + description: + 'Server supports GET reconnection with Last-Event-ID', + status: 'WARNING', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + statusCode: getResponse.status, + lastEventIdUsed: lastEventId, + message: `Server returned ${getResponse.status} for GET request with Last-Event-ID` + }, + errorMessage: `Server did not accept reconnection with Last-Event-ID (HTTP ${getResponse.status})` + }); + } + } + } else if (receivedToolResponse) { + // Tool response was received on the initial POST stream (server didn't disconnect) + checks.push({ + id: 'server-sse-disconnect-resume', + name: 'ServerDisconnectResume', + description: + 'Server closes SSE stream mid-call and resumes after reconnection', + status: 'INFO', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + receivedToolResponse: true, + message: + 'Tool response received on initial POST stream - server did not disconnect mid-call. The test_reconnection tool should close the stream before sending the result.' + } + }); + } else { + checks.push({ + id: 'server-sse-disconnect-resume', + name: 'ServerDisconnectResume', + description: + 'Server closes SSE stream mid-call and resumes after reconnection', + status: 'INFO', + timestamp: new Date().toISOString(), + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ], + details: { + lastEventId, + sessionId, + message: + 'Could not test disconnect/resume - no last event ID or session ID available' + } + }); + } + } catch (error) { + checks.push({ + id: 'server-sse-polling-error', + name: 'ServerSSEPollingTest', + description: 'Test server SSE polling behavior', + status: 'FAILURE', + timestamp: new Date().toISOString(), + errorMessage: `Error: ${error instanceof Error ? error.message : String(error)}`, + specReferences: [ + { + id: 'SEP-1699', + url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699' + } + ] + }); + } finally { + // Clean up + if (client) { + try { + await client.close(); + } catch { + // Ignore cleanup errors + } + } + } + + return checks; + } +} diff --git a/vitest.config.ts b/vitest.config.ts index 436dd00..93242b5 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ globals: true, environment: 'node', include: ['**/*.test.ts'], - exclude: ['node_modules', 'dist'], + exclude: ['**/node_modules/**', 'dist'], // Run test files sequentially to avoid port conflicts fileParallelism: false, // Increase timeout for server tests in CI