From 0f853e62bad7d5b189a8078bf19a9bf5591c72f9 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Sat, 31 Jan 2026 16:54:10 -0500 Subject: [PATCH 1/5] feat(mcp): Add AI-powered evaluation tests for MCP server tools Add vitest-evals based testing to verify AI assistants correctly select MCP tools. Uses Claude Sonnet 4.5 as the scoring model to predict which tools would be called for given user queries. Test coverage: - search-docs: 6 test cases for documentation search - get-doc: 5 test cases for fetching doc content - list-platforms: 6 test cases for platform discovery - get-doc-tree: 6 test cases for navigation structure All 23 tests pass with scores ranging from 0.85 to 1.0. Usage: - yarn eval: Run evals in watch mode - yarn eval:run: Run evals once Requires ANTHROPIC_API_KEY in .env.local Co-Authored-By: Claude --- package.json | 10 +- src/mcp/evals/setup.ts | 13 + src/mcp/evals/tests/get-doc-tree.eval.ts | 65 ++ src/mcp/evals/tests/get-doc.eval.ts | 57 ++ src/mcp/evals/tests/list-platforms.eval.ts | 65 ++ src/mcp/evals/tests/search-docs.eval.ts | 50 ++ src/mcp/evals/utils/index.ts | 2 + src/mcp/evals/utils/runner.ts | 13 + src/mcp/evals/utils/toolPredictionScorer.ts | 194 ++++++ src/mcp/evals/vitest.config.ts | 10 + yarn.lock | 671 +++++++++++++++++++- 11 files changed, 1144 insertions(+), 6 deletions(-) create mode 100644 src/mcp/evals/setup.ts create mode 100644 src/mcp/evals/tests/get-doc-tree.eval.ts create mode 100644 src/mcp/evals/tests/get-doc.eval.ts create mode 100644 src/mcp/evals/tests/list-platforms.eval.ts create mode 100644 src/mcp/evals/tests/search-docs.eval.ts create mode 100644 src/mcp/evals/utils/index.ts create mode 100644 src/mcp/evals/utils/runner.ts create mode 100644 src/mcp/evals/utils/toolPredictionScorer.ts create mode 100644 src/mcp/evals/vitest.config.ts diff --git a/package.json b/package.json index e9115480a6d72..f593f31a1b21d 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "sidecar": "yarn spotlight-sidecar", "test": "vitest", "test:ci": "vitest run", + "eval": "vitest --config=src/mcp/evals/vitest.config.ts", + "eval:run": "vitest run --config=src/mcp/evals/vitest.config.ts", "enforce-redirects": "node ./scripts/no-vercel-json-redirects.mjs" }, "dependencies": { @@ -45,6 +47,7 @@ "@google-cloud/storage": "^7.7.0", "@mdx-js/loader": "^3.0.0", "@mdx-js/react": "^3.0.0", + "@modelcontextprotocol/sdk": "^1.25.3", "@pondorasti/remark-img-links": "^1.0.8", "@popperjs/core": "^2.11.8", "@prettier/plugin-xml": "^3.3.1", @@ -73,6 +76,7 @@ "js-cookie": "^3.0.5", "js-yaml": "^4.1.0", "match-sorter": "^6.3.4", + "mcp-handler": "^1.0.7", "mdx-bundler": "^10.0.1", "mermaid": "^11.11.0", "micromark": "^4.0.0", @@ -113,9 +117,11 @@ "tailwindcss-scoped-preflight": "^3.0.4", "textarea-markdown-editor": "^1.0.4", "unified": "^11.0.5", - "unist-util-remove": "^4.0.0" + "unist-util-remove": "^4.0.0", + "zod": "^4.3.6" }, "devDependencies": { + "@ai-sdk/anthropic": "^3.0.33", "@babel/preset-typescript": "^7.15.0", "@codecov/nextjs-webpack-plugin": "^1.9.0", "@spotlightjs/spotlight": "^2.5.0", @@ -126,6 +132,7 @@ "@types/react": "18.3.12", "@types/react-dom": "18.3.1", "@types/ws": "^8.5.10", + "ai": "^6.0.65", "autoprefixer": "^10.4.17", "concurrently": "^9.1.0", "dotenv-cli": "^7.4.1", @@ -142,6 +149,7 @@ "typescript": "^5", "vite-tsconfig-paths": "^5.0.1", "vitest": "^3.0.7", + "vitest-evals": "^0.5.0", "ws": "^8.17.1" }, "resolutions": { diff --git a/src/mcp/evals/setup.ts b/src/mcp/evals/setup.ts new file mode 100644 index 0000000000000..37e8c41cae2fa --- /dev/null +++ b/src/mcp/evals/setup.ts @@ -0,0 +1,13 @@ +import {config} from 'dotenv'; + +// Load environment variables from .env.local or .env +config({path: '.env.local'}); +config({path: '.env'}); + +// Verify ANTHROPIC_API_KEY is set +if (!process.env.ANTHROPIC_API_KEY) { + console.warn( + 'Warning: ANTHROPIC_API_KEY is not set. Evals will fail without it.\n' + + 'Set it in .env.local: ANTHROPIC_API_KEY=sk-ant-...' + ); +} diff --git a/src/mcp/evals/tests/get-doc-tree.eval.ts b/src/mcp/evals/tests/get-doc-tree.eval.ts new file mode 100644 index 0000000000000..2b3008716c4a7 --- /dev/null +++ b/src/mcp/evals/tests/get-doc-tree.eval.ts @@ -0,0 +1,65 @@ +import {describeEval} from 'vitest-evals'; + +import {NoOpTaskRunner, ToolPredictionScorer} from '../utils'; + +describeEval('get-doc-tree', { + data: async () => [ + { + input: 'Show me the documentation structure', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {}, + }, + ], + }, + { + input: 'What sections are available in the JavaScript documentation?', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {path: 'platforms/javascript'}, + }, + ], + }, + { + input: 'Navigate the Python SDK docs, show me the table of contents', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {path: 'platforms/python'}, + }, + ], + }, + { + input: 'I want to explore the configuration options, show me what subsections exist', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {path: 'configuration', depth: 2}, + }, + ], + }, + { + input: 'Give me a deep overview of all pages under product/sentry-basics', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {path: 'product/sentry-basics', depth: 3}, + }, + ], + }, + { + input: 'Show me just the top-level categories of documentation', + expectedTools: [ + { + name: 'get_doc_tree', + arguments: {depth: 1}, + }, + ], + }, + ], + task: NoOpTaskRunner(), + scorers: [ToolPredictionScorer()], + threshold: 0.6, +}); diff --git a/src/mcp/evals/tests/get-doc.eval.ts b/src/mcp/evals/tests/get-doc.eval.ts new file mode 100644 index 0000000000000..300694ffef4de --- /dev/null +++ b/src/mcp/evals/tests/get-doc.eval.ts @@ -0,0 +1,57 @@ +import {describeEval} from 'vitest-evals'; + +import {NoOpTaskRunner, ToolPredictionScorer} from '../utils'; + +describeEval('get-doc', { + data: async () => [ + { + input: 'Get the full documentation for /platforms/javascript/guides/nextjs', + expectedTools: [ + { + name: 'get_doc', + arguments: {path: '/platforms/javascript/guides/nextjs'}, + }, + ], + }, + { + input: 'Read the Python SDK setup guide at platforms/python', + expectedTools: [ + { + name: 'get_doc', + arguments: {path: 'platforms/python'}, + }, + ], + }, + { + input: 'Fetch the configuration page for the Go SDK', + expectedTools: [ + { + name: 'get_doc', + arguments: {path: '/platforms/go/configuration'}, + }, + ], + }, + { + input: 'Get the developer documentation for envelope formats', + expectedTools: [ + { + name: 'get_doc', + arguments: {path: 'envelope', site: 'develop'}, + }, + ], + }, + { + input: + "I found a search result for /platforms/javascript/configuration/sampling, can you get the full content?", + expectedTools: [ + { + name: 'get_doc', + arguments: {path: '/platforms/javascript/configuration/sampling'}, + }, + ], + }, + ], + task: NoOpTaskRunner(), + scorers: [ToolPredictionScorer()], + threshold: 0.6, +}); diff --git a/src/mcp/evals/tests/list-platforms.eval.ts b/src/mcp/evals/tests/list-platforms.eval.ts new file mode 100644 index 0000000000000..e5b173bd6ed9b --- /dev/null +++ b/src/mcp/evals/tests/list-platforms.eval.ts @@ -0,0 +1,65 @@ +import {describeEval} from 'vitest-evals'; + +import {NoOpTaskRunner, ToolPredictionScorer} from '../utils'; + +describeEval('list-platforms', { + data: async () => [ + { + input: 'What platforms does Sentry support?', + expectedTools: [ + { + name: 'list_platforms', + arguments: {}, + }, + ], + }, + { + input: 'List all available SDKs', + expectedTools: [ + { + name: 'list_platforms', + arguments: {}, + }, + ], + }, + { + input: 'What frameworks are available for JavaScript?', + expectedTools: [ + { + name: 'list_platforms', + arguments: {platform: 'javascript'}, + }, + ], + }, + { + input: 'Show me the guides available for the Python SDK', + expectedTools: [ + { + name: 'list_platforms', + arguments: {platform: 'python'}, + }, + ], + }, + { + input: 'Does Sentry support React Native?', + expectedTools: [ + { + name: 'list_platforms', + arguments: {platform: 'react-native'}, + }, + ], + }, + { + input: 'What mobile SDKs does Sentry have?', + expectedTools: [ + { + name: 'list_platforms', + arguments: {}, + }, + ], + }, + ], + task: NoOpTaskRunner(), + scorers: [ToolPredictionScorer()], + threshold: 0.6, +}); diff --git a/src/mcp/evals/tests/search-docs.eval.ts b/src/mcp/evals/tests/search-docs.eval.ts new file mode 100644 index 0000000000000..17d0312a6087f --- /dev/null +++ b/src/mcp/evals/tests/search-docs.eval.ts @@ -0,0 +1,50 @@ +import {describeEval} from 'vitest-evals'; + +import {NoOpTaskRunner, ToolPredictionScorer} from '../utils'; + +describeEval('search-docs', { + data: async () => [ + { + input: 'How do I set up Sentry in my Next.js app?', + expectedTools: [ + { + name: 'search_docs', + arguments: {query: 'setup Next.js', guide: 'javascript/nextjs'}, + }, + ], + }, + { + input: 'Show me the error handling configuration for Python', + expectedTools: [{name: 'search_docs', arguments: {query: 'error handling Python'}}], + }, + { + input: 'How do I configure the tracesSampleRate option?', + expectedTools: [{name: 'search_docs', arguments: {query: 'tracesSampleRate configuration'}}], + }, + { + input: 'Find documentation about beforeSend hook', + expectedTools: [{name: 'search_docs', arguments: {query: 'beforeSend hook'}}], + }, + { + input: 'How do I add breadcrumbs to my errors in React?', + expectedTools: [ + { + name: 'search_docs', + arguments: {query: 'breadcrumbs', guide: 'javascript/react'}, + }, + ], + }, + { + input: 'Search the developer docs for SDK architecture', + expectedTools: [ + { + name: 'search_docs', + arguments: {query: 'SDK architecture', site: 'develop'}, + }, + ], + }, + ], + task: NoOpTaskRunner(), + scorers: [ToolPredictionScorer()], + threshold: 0.6, +}); diff --git a/src/mcp/evals/utils/index.ts b/src/mcp/evals/utils/index.ts new file mode 100644 index 0000000000000..af34693b5b620 --- /dev/null +++ b/src/mcp/evals/utils/index.ts @@ -0,0 +1,2 @@ +export {NoOpTaskRunner} from './runner'; +export {ToolPredictionScorer, type ExpectedToolCall} from './toolPredictionScorer'; diff --git a/src/mcp/evals/utils/runner.ts b/src/mcp/evals/utils/runner.ts new file mode 100644 index 0000000000000..5193211bbae67 --- /dev/null +++ b/src/mcp/evals/utils/runner.ts @@ -0,0 +1,13 @@ +/** + * NoOpTaskRunner - A task runner that does nothing. + * + * In tool prediction evals, we don't actually execute the tools. + * We just verify that the AI correctly predicts which tools would be called. + * The scorer handles the actual prediction logic. + */ +export function NoOpTaskRunner() { + return async function runner(input: string) { + // Simply return the input - the actual evaluation happens in the scorer + return input; + }; +} diff --git a/src/mcp/evals/utils/toolPredictionScorer.ts b/src/mcp/evals/utils/toolPredictionScorer.ts new file mode 100644 index 0000000000000..4ac2a7ce41065 --- /dev/null +++ b/src/mcp/evals/utils/toolPredictionScorer.ts @@ -0,0 +1,194 @@ +import {createAnthropic} from '@ai-sdk/anthropic'; +import {generateObject, type LanguageModel} from 'ai'; +import {z} from 'zod'; + +const anthropicProvider = createAnthropic(); + +/** + * Expected tool call structure for evaluation. + */ +export interface ExpectedToolCall { + /** Tool name (e.g., "search_docs", "get_doc") */ + name: string; + /** Expected arguments - can be partial, only checked fields are compared */ + arguments?: Record; +} + +/** + * Available tools in the sentry-docs MCP server. + * This description helps the scoring model understand what tools are available. + */ +const AVAILABLE_TOOLS = [ + { + name: 'search_docs', + description: + 'Search Sentry documentation for SDK setup, configuration, and usage guidance. Returns snippets with relevance scores.', + parameters: { + query: 'Search query in natural language (required)', + maxResults: 'Maximum results to return, 1-10 (optional, default 5)', + site: 'Which site to search: "docs" or "develop" (optional, default "docs")', + guide: + 'Filter by platform/guide, e.g., "javascript/nextjs", "python/django" (optional)', + }, + }, + { + name: 'get_doc', + description: + 'Fetch the full markdown content of a Sentry documentation page. Use after search_docs to get complete content.', + parameters: { + path: 'Documentation path, e.g., "/platforms/javascript/guides/nextjs" (required)', + site: 'Which site: "docs" or "develop" (optional, default "docs")', + }, + }, + { + name: 'list_platforms', + description: + 'List available SDK platforms and their guides in Sentry documentation. Without platform param returns all platforms, with platform param returns detailed info including guides.', + parameters: { + platform: + 'Specific platform slug to get details for, e.g., "javascript", "python" (optional)', + }, + }, + { + name: 'get_doc_tree', + description: + 'Get the documentation structure/table of contents for navigation. Helps understand documentation hierarchy.', + parameters: { + path: 'Starting path to get tree from, e.g., "platforms/javascript" (optional)', + depth: 'How many levels deep to include, 1-3 (optional, default 2)', + site: 'Which site: "docs" or "develop" (optional, default "docs")', + }, + }, +]; + +/** + * Schema for the prediction response from the scoring model. + */ +const predictionSchema = z.object({ + predictedTools: z.array( + z.object({ + name: z.string().describe('The name of the tool that would be called'), + argumentsJson: z + .string() + .optional() + .describe('JSON string of the arguments that would be passed to the tool'), + reasoning: z + .string() + .describe('Brief explanation of why this tool would be selected'), + }) + ), + score: z + .number() + .describe('Score from 0.0 to 1.0 indicating how well the predicted tools match the expected'), + rationale: z + .string() + .describe('Explanation of the score and any discrepancies between predicted and expected'), +}); + +/** + * Generate the prompt for the scoring model. + */ +function generatePrompt( + userQuery: string, + expectedTools: ExpectedToolCall[] +): string { + const toolDescriptions = AVAILABLE_TOOLS.map( + tool => + `- **${tool.name}**: ${tool.description}\n Parameters: ${JSON.stringify(tool.parameters, null, 2)}` + ).join('\n\n'); + + const expectedDescription = expectedTools + .map( + (tool, i) => + `${i + 1}. Tool: ${tool.name}${tool.arguments ? `\n Arguments: ${JSON.stringify(tool.arguments)}` : ''}` + ) + .join('\n'); + + return `You are evaluating whether an AI assistant would correctly select MCP tools to answer a user query about Sentry documentation. + +## Available Tools + +${toolDescriptions} + +## User Query + +"${userQuery}" + +## Expected Tool Calls + +The expected tool calls for this query are: + +${expectedDescription} + +## Your Task + +1. Predict which tool(s) an AI assistant would call to answer the user's query. +2. Consider the most logical and efficient tool selection. +3. Compare your prediction against the expected tools. +4. Score how well the expected tools match what should be called: + - 1.0: Perfect match - the expected tools are exactly what should be called + - 0.8-0.9: Good match - minor differences in arguments or an extra optional tool + - 0.6-0.7: Acceptable - correct primary tool but missing secondary tools or wrong arguments + - 0.3-0.5: Partial match - some correct tools but significant issues + - 0.0-0.2: Poor match - wrong tools or completely off + +Focus on evaluating whether the EXPECTED tools make sense for the query, not whether you would choose differently.`; +} + +/** + * Default model for scoring - Claude Sonnet 4.5 for balance of quality and cost. + */ +const defaultModel = anthropicProvider('claude-sonnet-4-5-20250929'); + +/** + * Scorer options interface matching vitest-evals BaseScorerOptions + * with our custom expectedTools field. + */ +interface ToolPredictionScorerOptions { + input: string; + output: string; + expectedTools?: ExpectedToolCall[]; +} + +/** + * ToolPredictionScorer - Scores tool prediction accuracy using Claude. + * + * This scorer asks Claude to predict which tools would be called for a given + * user query, then compares against the expected tools to generate a score. + * + * @param model - Optional LanguageModel to use for scoring (defaults to Claude Sonnet) + * @returns Scorer function compatible with vitest-evals + */ +export function ToolPredictionScorer(model: LanguageModel = defaultModel) { + return async function scorer(opts: ToolPredictionScorerOptions) { + // If no expected tools provided, we can't score + if (!opts.expectedTools || opts.expectedTools.length === 0) { + return {score: null}; + } + + try { + const {object} = await generateObject({ + model, + prompt: generatePrompt(opts.input, opts.expectedTools), + schema: predictionSchema, + }); + + return { + score: object.score, + metadata: { + rationale: object.rationale, + predictedTools: object.predictedTools, + expectedTools: opts.expectedTools, + }, + }; + } catch (error) { + console.error('Error in ToolPredictionScorer:', error); + return { + score: 0, + metadata: { + error: error instanceof Error ? error.message : 'Unknown error', + }, + }; + } + }; +} diff --git a/src/mcp/evals/vitest.config.ts b/src/mcp/evals/vitest.config.ts new file mode 100644 index 0000000000000..a631a80540117 --- /dev/null +++ b/src/mcp/evals/vitest.config.ts @@ -0,0 +1,10 @@ +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + root: 'src/mcp/evals', + include: ['**/*.eval.ts'], + reporters: ['vitest-evals/reporter'], + setupFiles: ['./setup.ts'], + }, +}); diff --git a/yarn.lock b/yarn.lock index 08e5e281a576d..e113565814e49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,6 +40,39 @@ resolved "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz" integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q== +"@ai-sdk/anthropic@^3.0.33": + version "3.0.33" + resolved "https://registry.yarnpkg.com/@ai-sdk/anthropic/-/anthropic-3.0.33.tgz#d92ef6a12f2fce69966e30c6bce093390dabce38" + integrity sha512-MvQzipOAK99Ct3nX6d8oz2AJ9c8kbiU2uSpGYK3F5oBBa8Gy04+LbYBoB9oH8bbZJxFafkgwBq1+k8xpU7vVxQ== + dependencies: + "@ai-sdk/provider" "3.0.6" + "@ai-sdk/provider-utils" "4.0.11" + +"@ai-sdk/gateway@3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-3.0.30.tgz#1f1e96aa997d3b4866e527001510ab5c8eba9fc9" + integrity sha512-AzvbnIltGIlmdcUXvkaOVRNWMTac/tKLPgqzzIVi9OAS0lxNxLSCuBGHWWRCORIBb3l6JQO5kB3wdzXpqNphpA== + dependencies: + "@ai-sdk/provider" "3.0.6" + "@ai-sdk/provider-utils" "4.0.11" + "@vercel/oidc" "3.1.0" + +"@ai-sdk/provider-utils@4.0.11": + version "4.0.11" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-4.0.11.tgz#dc25dc1cfb7c3b04d0b9460469859db2e684487b" + integrity sha512-y/WOPpcZaBjvNaogy83mBsCRPvbtaK0y1sY9ckRrrbTGMvG2HC/9Y/huqNXKnLAxUIME2PGa2uvF2CDwIsxoXQ== + dependencies: + "@ai-sdk/provider" "3.0.6" + "@standard-schema/spec" "^1.1.0" + eventsource-parser "^3.0.6" + +"@ai-sdk/provider@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-3.0.6.tgz#fd5812a82ee678989fa6e852e4a5e86591fa49bd" + integrity sha512-hSfoJtLtpMd7YxKM+iTqlJ0ZB+kJ83WESMiWuWrNVey3X8gg97x0OdAAaeAeclZByCX3UdPOTqhvJdK8qYA3ww== + dependencies: + json-schema "^0.4.0" + "@algolia/cache-browser-local-storage@4.24.0": version "4.24.0" resolved "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz" @@ -1624,6 +1657,11 @@ teeny-request "^9.0.0" uuid "^8.0.0" +"@hono/node-server@^1.19.9": + version "1.19.9" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.9.tgz#8f37119b1acf283fd3f6035f3d1356fdb97a09ac" + integrity sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw== + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz" @@ -2129,6 +2167,28 @@ dependencies: langium "3.3.1" +"@modelcontextprotocol/sdk@^1.25.3": + version "1.25.3" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz#a665ae5f983a5cdfe1a1809aafb48110b04faef1" + integrity sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.0.1" + express-rate-limit "^7.5.0" + jose "^6.1.1" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.0" + "@next/env@15.1.12": version "15.1.12" resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.12.tgz#0821510fa71871f1970cd216963d25a5970fe58a" @@ -2306,7 +2366,7 @@ dependencies: "@opentelemetry/api" "^1.3.0" -"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": +"@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -3426,6 +3486,40 @@ classnames "^2.3.2" react-remove-scroll-bar "^2.3.6" +"@redis/bloom@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" + integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== + +"@redis/client@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.6.1.tgz#c4636b7cb34e96008a988409b7e787364ae761a2" + integrity sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw== + dependencies: + cluster-key-slot "1.1.2" + generic-pool "3.9.0" + yallist "4.0.0" + +"@redis/graph@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.1.tgz#8c10df2df7f7d02741866751764031a957a170ea" + integrity sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw== + +"@redis/json@1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.7.tgz#016257fcd933c4cbcb9c49cde8a0961375c6893b" + integrity sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ== + +"@redis/search@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.2.0.tgz#50976fd3f31168f585666f7922dde111c74567b8" + integrity sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw== + +"@redis/time-series@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" + integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== + "@rollup/plugin-commonjs@28.0.1": version "28.0.1" resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz" @@ -4431,6 +4525,11 @@ "@spotlightjs/sidecar" "1.8.0" import-meta-resolve "^4.1.0" +"@standard-schema/spec@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + "@swc/counter@0.1.3": version "0.1.3" resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" @@ -5260,6 +5359,11 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vercel/oidc@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vercel/oidc/-/oidc-3.1.0.tgz#066caee449b84079f33c7445fc862464fe10ec32" + integrity sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w== + "@vitest/expect@3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.0.7.tgz#3490936bc1e97fc21d53441518d51cb7116c698a" @@ -5338,6 +5442,14 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" @@ -5405,6 +5517,23 @@ agent-base@^7.0.2: dependencies: debug "^4.3.4" +ai@^6.0.65: + version "6.0.65" + resolved "https://registry.yarnpkg.com/ai/-/ai-6.0.65.tgz#7af59846e5ec9301eecc8568d831f02a82f6784d" + integrity sha512-QosAz/c16GK9C7LH9ehK2r88J/UK1ZwCYK4FEMwF0JPJqT+P9ICLxq5x8D1f8sc9ydlu5tOeh8M6/siuZvFIkA== + dependencies: + "@ai-sdk/gateway" "3.0.30" + "@ai-sdk/provider" "3.0.6" + "@ai-sdk/provider-utils" "4.0.11" + "@opentelemetry/api" "1.9.0" + +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" @@ -5415,6 +5544,16 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + algoliasearch@^4.13.1, algoliasearch@^4.23.3: version "4.24.0" resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.24.0.tgz#b953b3e2309ef8f25da9de311b95b994ac918275" @@ -5803,6 +5942,21 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.2.tgz#1a32cdb966beaf68de50a9dfbe5b58f83cb8890c" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" @@ -5874,11 +6028,24 @@ busboy@1.6.0: dependencies: streamsearch "^1.1.0" +bytes@^3.1.2, bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + cac@^6.7.14: version "6.7.14" resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" @@ -5890,6 +6057,14 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -5939,6 +6114,11 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.3.0: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" @@ -6053,6 +6233,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +cluster-key-slot@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" @@ -6113,6 +6298,11 @@ commander@7: resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + commander@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" @@ -6156,6 +6346,16 @@ confbox@^0.2.2: resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.2.tgz#8652f53961c74d9e081784beed78555974a9c110" integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.1.tgz#a8b7bbeb2904befdfb6787e5c0c086959f605f9b" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + convert-source-map@^1.5.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" @@ -6166,6 +6366,24 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@^2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + cose-base@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz" @@ -6209,7 +6427,7 @@ create-require@^1.1.0: resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -6650,7 +6868,7 @@ debug@^4.4.0: dependencies: ms "^2.1.3" -debug@^4.4.1: +debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -6719,6 +6937,11 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + deprecation@^2.0.0: version "2.3.1" resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" @@ -6893,6 +7116,15 @@ dotenv@^16.3.0, dotenv@^16.3.1: resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexify@^4.1.3: version "4.1.3" resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz" @@ -6915,6 +7147,11 @@ ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: dependencies: safe-buffer "^5.0.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + electron-to-chromium@^1.5.41: version "1.5.64" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz" @@ -6935,6 +7172,11 @@ emoji-regex@^9.2.2: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" @@ -7026,6 +7268,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" @@ -7064,6 +7311,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" @@ -7596,11 +7850,28 @@ esutils@^2.0.2: resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1, eventsource-parser@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + execa@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" @@ -7637,6 +7908,45 @@ expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +express-rate-limit@^7.5.0: + version "7.5.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.1.tgz#8c3a42f69209a3a1c969890070ece9e20a879dec" + integrity sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw== + +express@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + exsolve@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.7.tgz#3b74e4c7ca5c5f9a19c3626ca857309fa99f9e9e" @@ -7691,6 +8001,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + fast-xml-parser@4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" @@ -7745,6 +8060,18 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" @@ -7833,6 +8160,11 @@ forwarded-parse@2.1.2: resolved "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz" integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw== +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" @@ -7847,6 +8179,11 @@ framer-motion@^10.12.16: optionalDependencies: "@emotion/is-prop-valid" "^0.8.2" +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -7896,6 +8233,11 @@ gcp-metadata@^6.1.0: gaxios "^6.0.0" json-bigint "^1.0.0" +generic-pool@3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" + integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" @@ -7917,6 +8259,22 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-nonce@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz" @@ -7927,6 +8285,14 @@ get-package-type@^0.1.0: resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" @@ -8064,6 +8430,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" @@ -8124,6 +8495,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" @@ -8567,6 +8943,17 @@ htmlparser2@^4.1.0: domutils "^2.0.0" entities "^2.0.0" +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" @@ -8620,6 +9007,13 @@ iconv-lite@0.6, iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" @@ -8681,7 +9075,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -8722,6 +9116,11 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz" @@ -8908,6 +9307,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-reference@1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz" @@ -9453,6 +9857,11 @@ jiti@^1.21.6: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +jose@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.3.tgz#8453d7be88af7bb7d64a0481d6a35a0145ba3ea5" + integrity sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ== + js-cookie@^3.0.5: version "3.0.5" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz" @@ -9570,6 +9979,16 @@ json-schema-traverse@^0.4.1: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-schema@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" @@ -9877,6 +10296,20 @@ match-sorter@^6.3.4: "@babel/runtime" "^7.23.8" remove-accents "0.5.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mcp-handler@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/mcp-handler/-/mcp-handler-1.0.7.tgz#1f038b1e2b17ac5d890cf4e24ee3e9d4f2d2d3c0" + integrity sha512-w2wHb6IVmbiS+pnBNb5BXaSd+ynSgExNauB55gUwoHDw8Q8Ew9TVMsSX89yItmex61zQTl+/NuSYmlOgSpj8SQ== + dependencies: + chalk "^5.3.0" + commander "^11.1.0" + redis "^4.6.0" + mdast-util-definitions@^5.0.0: version "5.1.2" resolved "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz" @@ -10216,11 +10649,21 @@ mdx-bundler@^10.0.1: uuid "^9.0.1" vfile "^6.0.1" +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + memoize-one@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" @@ -10937,6 +11380,11 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-db@~1.25.0: version "1.25.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz" @@ -10956,6 +11404,13 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" @@ -11061,6 +11516,11 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + next-mdx-remote@^4.4.1: version "4.4.1" resolved "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz" @@ -11168,7 +11628,7 @@ nwsapi@^2.2.0, nwsapi@^2.2.2: resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz" integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ== -object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -11240,6 +11700,13 @@ obuf@~1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -11371,6 +11838,11 @@ parse5@^7.0.0, parse5@^7.1.1: dependencies: entities "^4.5.0" +parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-data-parser@0.1.0, path-data-parser@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz" @@ -11404,6 +11876,11 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -11492,6 +11969,11 @@ pirates@^4.0.1, pirates@^4.0.4: resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -11723,6 +12205,14 @@ property-information@^7.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== +proxy-addr@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" @@ -11745,6 +12235,13 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +qs@^6.14.0, qs@^6.14.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159" + integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== + dependencies: + side-channel "^1.1.0" + quansync@^0.2.11: version "0.2.11" resolved "https://registry.yarnpkg.com/quansync/-/quansync-0.2.11.tgz#f9c3adda2e1272e4f8cf3f1457b04cbdb4ee692a" @@ -11767,6 +12264,21 @@ queue@6.0.2: dependencies: inherits "~2.0.3" +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + react-dom@^19.2.4: version "19.2.4" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.4.tgz#6fac6bd96f7db477d966c7ec17c1a2b1ad8e6591" @@ -11966,6 +12478,18 @@ recma-stringify@^1.0.0: unified "^11.0.0" vfile "^6.0.0" +redis@^4.6.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/redis/-/redis-4.7.1.tgz#08588a30936be0e7ad9c0f3e1ac6a85ccaf73e94" + integrity sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ== + dependencies: + "@redis/bloom" "1.2.0" + "@redis/client" "1.6.1" + "@redis/graph" "1.1.1" + "@redis/json" "1.0.7" + "@redis/search" "1.2.0" + "@redis/time-series" "1.1.0" + reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz" @@ -12482,6 +13006,11 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-in-the-middle@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz#dbde2587f669398626d56b20c868ab87bf01cce4" @@ -12643,6 +13172,17 @@ roughjs@^4.6.6: points-on-curve "^0.2.0" points-on-path "^0.2.1" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + rss@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz" @@ -12759,6 +13299,33 @@ semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + server-only@^0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz" @@ -12786,6 +13353,11 @@ set-function-name@^2.0.1, set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + sharp@^0.33.4, sharp@^0.33.5: version "0.33.5" resolved "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz" @@ -12837,6 +13409,35 @@ shimmer@^1.2.1: resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" @@ -12847,6 +13448,17 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + siginfo@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" @@ -12936,6 +13548,11 @@ stacktrace-parser@^0.1.10: dependencies: type-fest "^0.7.1" +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + std-env@^3.8.0: version "3.8.0" resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz" @@ -13333,6 +13950,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + toml@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" @@ -13485,6 +14107,15 @@ type-fest@^0.7.1: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" @@ -13759,6 +14390,11 @@ universalify@^0.2.0: resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + unplugin@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/unplugin/-/unplugin-1.0.1.tgz" @@ -13896,6 +14532,11 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +vary@^1, vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + vfile-location@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz" @@ -13986,6 +14627,11 @@ vite-tsconfig-paths@^5.0.1: optionalDependencies: fsevents "~2.3.3" +vitest-evals@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/vitest-evals/-/vitest-evals-0.5.0.tgz#543f3c59ad7ffe7f05437d85666836ffa1512a9f" + integrity sha512-kg8r6NKNBD6jkmyjdO64qduATW6IUG+62atCJj3dLzaRZEG4TIdfFVy64iEh0dSSo1CCpAJi+dJqSN2Y2qv2Sw== + vitest@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.0.7.tgz#ed8f42e1b0e09e2179eaefd966cb58a8b75f0f6a" @@ -14321,6 +14967,11 @@ y18n@^5.0.5: resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" @@ -14369,11 +15020,21 @@ yocto-queue@^1.1.1: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418" integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg== +zod-to-json-schema@^3.25.0: + version "3.25.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba" + integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + zod@^3.22.4: version "3.23.8" resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== +"zod@^3.25 || ^4.0", zod@^4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" + integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== + zwitch@^2.0.0, zwitch@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" From 042911d3068a635bc1dfa4c1148640913146d56c Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Sat, 31 Jan 2026 16:55:26 -0500 Subject: [PATCH 2/5] feat(mcp): Add MCP server implementation for Sentry docs Add Model Context Protocol server exposing Sentry documentation to AI assistants. Provides 4 tools: - search_docs: Search documentation with optional platform filtering - get_doc: Fetch full markdown content of a documentation page - list_platforms: List available SDK platforms and their guides - get_doc_tree: Get documentation structure for navigation Endpoint: /mcp (HTTP Streamable transport) Co-Authored-By: Claude --- app/mcp/route.ts | 23 +++++ next.config.ts | 1 + src/mcp/index.ts | 13 +++ src/mcp/tools/get-doc-tree.ts | 158 ++++++++++++++++++++++++++++++++ src/mcp/tools/get-doc.ts | 139 ++++++++++++++++++++++++++++ src/mcp/tools/list-platforms.ts | 109 ++++++++++++++++++++++ src/mcp/tools/search-docs.ts | 101 ++++++++++++++++++++ src/mcp/types.ts | 28 ++++++ src/middleware.ts | 31 ++++++- 9 files changed, 600 insertions(+), 3 deletions(-) create mode 100644 app/mcp/route.ts create mode 100644 src/mcp/index.ts create mode 100644 src/mcp/tools/get-doc-tree.ts create mode 100644 src/mcp/tools/get-doc.ts create mode 100644 src/mcp/tools/list-platforms.ts create mode 100644 src/mcp/tools/search-docs.ts create mode 100644 src/mcp/types.ts diff --git a/app/mcp/route.ts b/app/mcp/route.ts new file mode 100644 index 0000000000000..ff07c62918066 --- /dev/null +++ b/app/mcp/route.ts @@ -0,0 +1,23 @@ +import {createMcpHandler} from 'mcp-handler'; + +import {registerTools} from '../../src/mcp'; + +// Trailing slash handling is done in middleware +const handler = createMcpHandler( + server => { + registerTools(server); + }, + { + serverInfo: { + name: 'sentry-docs-mcp', + version: '1.0.0', + }, + }, + { + basePath: '/', + maxDuration: 60, + disableSse: true, // Stateless HTTP only - no Redis required + } +); + +export {handler as GET, handler as POST}; diff --git a/next.config.ts b/next.config.ts index b89fa77ad5236..a021bd451815f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -130,6 +130,7 @@ if (process.env.NODE_ENV !== 'development' && !process.env.NEXT_PUBLIC_SENTRY_DS const nextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx', 'mdx'], trailingSlash: true, + skipTrailingSlashRedirect: true, serverExternalPackages: [ 'rehype-preset-minify', 'esbuild', diff --git a/src/mcp/index.ts b/src/mcp/index.ts new file mode 100644 index 0000000000000..1868c6bda1fc2 --- /dev/null +++ b/src/mcp/index.ts @@ -0,0 +1,13 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; + +import {registerGetDoc} from './tools/get-doc'; +import {registerGetDocTree} from './tools/get-doc-tree'; +import {registerListPlatforms} from './tools/list-platforms'; +import {registerSearchDocs} from './tools/search-docs'; + +export function registerTools(server: McpServer) { + registerSearchDocs(server); + registerGetDoc(server); + registerListPlatforms(server); + registerGetDocTree(server); +} diff --git a/src/mcp/tools/get-doc-tree.ts b/src/mcp/tools/get-doc-tree.ts new file mode 100644 index 0000000000000..7d6bc3cd960af --- /dev/null +++ b/src/mcp/tools/get-doc-tree.ts @@ -0,0 +1,158 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +import {z} from 'zod'; + +import type {DocNode} from 'sentry-docs/docTree'; +import {getDocsRootNode, nodeForPath} from 'sentry-docs/docTree'; + +interface TreeNode { + path: string; + title: string; + children?: TreeNode[]; +} + +function filterTreeByDepth(node: DocNode, currentDepth: number, maxDepth: number): TreeNode { + const title = node.frontmatter.sidebar_title || node.frontmatter.title || node.slug; + + const result: TreeNode = { + path: node.path, + title, + }; + + if (currentDepth < maxDepth && node.children && node.children.length > 0) { + const visibleChildren = node.children.filter( + child => + !child.frontmatter.sidebar_hidden && + !child.frontmatter.draft && + (child.frontmatter.sidebar_title || child.frontmatter.title) + ); + + if (visibleChildren.length > 0) { + result.children = visibleChildren.map(child => + filterTreeByDepth(child, currentDepth + 1, maxDepth) + ); + } + } + + return result; +} + +function treeToMarkdown(node: TreeNode, indent: number = 0): string { + const prefix = ' '.repeat(indent); + let output = `${prefix}- **${node.title}** \`${node.path || '/'}\`\n`; + + if (node.children) { + for (const child of node.children) { + output += treeToMarkdown(child, indent + 1); + } + } + + return output; +} + +export function registerGetDocTree(server: McpServer) { + const description = [ + 'Get the documentation structure/table of contents for navigation.', + '', + 'Use this tool to:', + '- Understand the documentation hierarchy', + '- Find available sections and subsections', + '- Navigate to related documentation pages', + '', + '', + '- Without path: returns top-level structure', + '- With path: returns structure starting from that path', + '- Use depth to control how deep to traverse (1-3)', + '', + ].join('\n'); + + const paramsSchema = { + path: z + .string() + .optional() + .describe('Starting path to get tree from (e.g., "platforms/javascript")'), + depth: z + .number() + .int() + .min(1) + .max(3) + .default(2) + .describe('How many levels deep to include (1-3)'), + site: z + .enum(['docs', 'develop']) + .default('docs') + .describe('Which site: docs or develop'), + }; + + server.tool('get_doc_tree', description, paramsSchema, async ({path, depth, site}) => { + try { + // Note: For develop docs, we'd need getDevelopDocsRootNode but it's not exported + // For now, we only fully support docs site + if (site === 'develop') { + return { + content: [ + { + type: 'text' as const, + text: [ + '# Developer Docs Tree', + '', + 'The doc tree for develop.sentry.io is not available in this context.', + 'Use `search_docs(site="develop")` to search developer documentation.', + ].join('\n'), + }, + ], + }; + } + + const rootNode = await getDocsRootNode(); + + let startNode = rootNode; + if (path) { + const found = nodeForPath(rootNode, path); + if (!found) { + return { + content: [ + { + type: 'text' as const, + text: [ + `# Path Not Found: ${path}`, + '', + 'The specified path does not exist in the documentation tree.', + 'Use `get_doc_tree()` without a path to see the top-level structure.', + ].join('\n'), + }, + ], + }; + } + startNode = found; + } + + const tree = filterTreeByDepth(startNode, 0, depth); + + let output = `# Documentation Structure\n\n`; + if (path) { + output += `**Starting from**: \`${path}\`\n`; + } + output += `**Depth**: ${depth} level(s)\n\n`; + + output += treeToMarkdown(tree); + + output += '\n## Navigation Tips\n\n'; + output += '- Use `get_doc(path="...")` to read a specific page\n'; + output += '- Use `get_doc_tree(path="...")` to explore a subsection\n'; + output += '- Use `search_docs(query="...")` to find specific topics\n'; + + return { + content: [{type: 'text' as const, text: output}], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting doc tree: ${error instanceof Error ? error.message : 'Unknown error'}`, + }, + ], + }; + } + }); +} diff --git a/src/mcp/tools/get-doc.ts b/src/mcp/tools/get-doc.ts new file mode 100644 index 0000000000000..990f1fcd74107 --- /dev/null +++ b/src/mcp/tools/get-doc.ts @@ -0,0 +1,139 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +import {z} from 'zod'; + +const MAX_CONTENT_LENGTH = 10000; // 10KB limit to avoid token bloat + +export function registerGetDoc(server: McpServer) { + const description = [ + 'Fetch the full markdown content of a Sentry documentation page.', + '', + 'Use this tool when you need to:', + '- Read complete documentation for a topic', + '- Get detailed code examples and configuration', + '- Access full context after finding pages via search_docs', + '', + '', + '- Use paths from search_docs results', + '- Paths like "/platforms/javascript/guides/nextjs"', + '- Content over 10KB will be truncated', + '', + ].join('\n'); + + const paramsSchema = { + path: z + .string() + .describe( + 'Documentation path (e.g., "/platforms/javascript/guides/nextjs"). Get from search_docs.' + ), + site: z + .enum(['docs', 'develop']) + .default('docs') + .describe('Which site: docs (docs.sentry.io) or develop (develop.sentry.io)'), + }; + + server.tool('get_doc', description, paramsSchema, async ({path, site}) => { + // Normalize path - remove leading slash if present, remove trailing slash + let normalizedPath = path.replace(/^\/+/, '').replace(/\/+$/, ''); + + // Don't add .md if path already has it + if (!normalizedPath.endsWith('.md')) { + normalizedPath = `${normalizedPath}.md`; + } + + const baseUrl = + site === 'develop' ? 'https://develop.sentry.io' : 'https://docs.sentry.io'; + + const docUrl = `${baseUrl}/${normalizedPath}`; + + try { + const response = await fetch(docUrl, { + headers: { + Accept: 'text/plain, text/markdown', + 'User-Agent': 'sentry-docs-mcp/1.0', + }, + }); + + if (!response.ok) { + if (response.status === 404) { + return { + content: [ + { + type: 'text' as const, + text: [ + `# Document Not Found`, + '', + `**Path**: ${path}`, + `**URL**: ${docUrl}`, + '', + 'The document was not found. Suggestions:', + '- Verify the path is correct (check search_docs results)', + '- Try without file extension in the path', + '- The page may have been moved or renamed', + '', + 'Use `search_docs` to find the correct path.', + ].join('\n'), + }, + ], + }; + } + throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); + } + + let content = await response.text(); + + // Check if we got HTML instead of markdown + if (content.trim().startsWith(' MAX_CONTENT_LENGTH; + if (wasTruncated) { + content = content.slice(0, MAX_CONTENT_LENGTH); + // Try to truncate at a natural break point + const lastNewline = content.lastIndexOf('\n\n'); + if (lastNewline > MAX_CONTENT_LENGTH * 0.8) { + content = content.slice(0, lastNewline); + } + } + + let output = `# Documentation: ${path}\n\n`; + output += `**Source**: ${docUrl}\n`; + if (wasTruncated) { + output += `**Note**: Content truncated (over 10KB). View full doc at the URL above.\n`; + } + output += '\n---\n\n'; + output += content; + output += '\n\n---\n'; + + return { + content: [{type: 'text' as const, text: output}], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error fetching document: ${error instanceof Error ? error.message : 'Unknown error'}`, + }, + ], + }; + } + }); +} diff --git a/src/mcp/tools/list-platforms.ts b/src/mcp/tools/list-platforms.ts new file mode 100644 index 0000000000000..7d2f2054bd829 --- /dev/null +++ b/src/mcp/tools/list-platforms.ts @@ -0,0 +1,109 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +import {z} from 'zod'; + +import {extractPlatforms, getDocsRootNode} from 'sentry-docs/docTree'; + +export function registerListPlatforms(server: McpServer) { + const description = [ + 'List available SDK platforms and their guides in Sentry documentation.', + '', + 'Use this tool to:', + '- Discover all supported platforms (JavaScript, Python, Go, etc.)', + '- Find available framework guides for a platform', + '- Get platform slugs for filtering search_docs queries', + '', + '', + '- Without platform param: returns list of all platforms', + '- With platform param: returns detailed info including guides', + '', + ].join('\n'); + + const paramsSchema = { + platform: z + .string() + .optional() + .describe('Specific platform slug to get details for (e.g., "javascript", "python")'), + }; + + server.tool('list_platforms', description, paramsSchema, async ({platform}) => { + try { + const rootNode = await getDocsRootNode(); + const platforms = extractPlatforms(rootNode); + + if (platform) { + // Return details for specific platform + const found = platforms.find(p => p.key === platform || p.name === platform); + + if (!found) { + const availablePlatforms = platforms.map(p => p.key).join(', '); + return { + content: [ + { + type: 'text' as const, + text: [ + `# Platform Not Found: ${platform}`, + '', + `Available platforms: ${availablePlatforms}`, + ].join('\n'), + }, + ], + }; + } + + let output = `# Platform: ${found.title}\n\n`; + output += `**Slug**: ${found.key}\n`; + output += `**URL**: ${found.url}\n`; + if (found.sdk) { + output += `**SDK**: ${found.sdk}\n`; + } + if (found.categories?.length) { + output += `**Categories**: ${found.categories.join(', ')}\n`; + } + output += '\n'; + + if (found.guides && found.guides.length > 0) { + output += `## Guides (${found.guides.length})\n\n`; + for (const guide of found.guides) { + output += `- **${guide.title}** - \`${guide.key}\`\n`; + output += ` URL: ${guide.url}\n`; + } + } else { + output += '*No framework-specific guides available for this platform.*\n'; + } + + return { + content: [{type: 'text' as const, text: output}], + }; + } + + // Return list of all platforms + let output = `# Available Platforms\n\n`; + output += `Found ${platforms.length} platforms. Use \`list_platforms(platform='...')\` for details.\n\n`; + + for (const p of platforms) { + const guidesCount = p.guides?.length || 0; + const guidesInfo = guidesCount > 0 ? ` (${guidesCount} guides)` : ''; + output += `- **${p.title}** - \`${p.key}\`${guidesInfo}\n`; + } + + output += '\n## Usage\n\n'; + output += 'Use platform slugs with `search_docs(guide="platform/framework")`\n'; + output += 'Examples:\n'; + output += '- `search_docs(query="setup", guide="javascript/nextjs")`\n'; + output += '- `search_docs(query="configuration", guide="python/django")`\n'; + + return { + content: [{type: 'text' as const, text: output}], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing platforms: ${error instanceof Error ? error.message : 'Unknown error'}`, + }, + ], + }; + } + }); +} diff --git a/src/mcp/tools/search-docs.ts b/src/mcp/tools/search-docs.ts new file mode 100644 index 0000000000000..c02fd1199e41c --- /dev/null +++ b/src/mcp/tools/search-docs.ts @@ -0,0 +1,101 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +import {SentryGlobalSearch, standardSDKSlug} from '@sentry-internal/global-search'; +import {z} from 'zod'; + +const docsSearch = new SentryGlobalSearch([{site: 'docs', pathBias: true, platformBias: true}]); +const developSearch = new SentryGlobalSearch(['develop']); + +export function registerSearchDocs(server: McpServer) { + const description = [ + 'Search Sentry documentation for SDK setup, configuration, and usage guidance.', + '', + 'Use this tool when you need to:', + '- Find SDK setup instructions for any platform or framework', + '- Look up configuration options and API references', + '- Find code examples and implementation patterns', + '', + 'Returns snippets with relevance scores. Use `get_doc` to fetch full content.', + '', + '', + '- Use guide parameter to filter by platform (e.g., "javascript/nextjs")', + '- Include specific terms like "beforeSend", "tracesSampleRate", "DSN"', + '', + ].join('\n'); + + const paramsSchema = { + query: z + .string() + .min(2, 'Query too short, minimum 2 characters') + .max(200, 'Query too long, maximum 200 characters') + .describe('Search query in natural language'), + maxResults: z + .number() + .int() + .min(1) + .max(10) + .default(5) + .describe('Maximum results to return (1-10)'), + site: z + .enum(['docs', 'develop']) + .default('docs') + .describe('Which site to search: docs (user docs) or develop (SDK dev docs)'), + guide: z + .string() + .optional() + .describe('Filter by platform/guide (e.g., "javascript/nextjs", "python/django")'), + }; + + server.tool('search_docs', description, paramsSchema, async ({query, maxResults, site, guide}) => { + const search = site === 'develop' ? developSearch : docsSearch; + const platforms = guide ? [standardSDKSlug(guide)?.slug].filter(Boolean) : []; + + const results = await search.query(query, { + platforms: platforms as string[], + searchAllIndexes: false, + }); + + // Flatten and format results + const allHits = results.flatMap(result => + result.hits.map(hit => ({ + id: hit.id, + url: hit.url, + title: hit.title?.replace(/<[^>]*>/g, '') || '', + snippet: + hit.text?.replace(/<[^>]*>/g, '').slice(0, 300) || + hit.context?.context1?.replace(/<[^>]*>/g, '').slice(0, 300) || + '', + })) + ); + + const limitedResults = allHits.slice(0, maxResults); + + if (limitedResults.length === 0) { + return { + content: [ + { + type: 'text' as const, + text: `No documentation found for "${query}". Try different search terms or check the spelling.`, + }, + ], + }; + } + + let output = `# Search Results for "${query}"\n\n`; + output += `Found ${limitedResults.length} result(s)\n\n`; + output += 'Use `get_doc` with the path to fetch full content.\n\n'; + + for (const [index, result] of limitedResults.entries()) { + const path = new URL(result.url).pathname.replace(/\/$/, ''); + output += `## ${index + 1}. ${result.title || 'Untitled'}\n\n`; + output += `**Path**: \`${path}\`\n`; + output += `**URL**: ${result.url}\n\n`; + if (result.snippet) { + output += `> ${result.snippet.replace(/\n/g, '\n> ')}\n\n`; + } + } + + return { + content: [{type: 'text' as const, text: output}], + }; + }); +} diff --git a/src/mcp/types.ts b/src/mcp/types.ts new file mode 100644 index 0000000000000..d7677bd4a270d --- /dev/null +++ b/src/mcp/types.ts @@ -0,0 +1,28 @@ +export interface SearchResult { + id: string; + relevance: number; + snippet: string; + title: string; + url: string; +} + +export interface SearchResponse { + results: SearchResult[]; + error?: string; +} + +export interface DocTreeNode { + path: string; + title: string; + children?: DocTreeNode[]; +} + +export interface PlatformInfo { + slug: string; + title: string; + guides?: Array<{ + slug: string; + title: string; + }>; + guidesCount?: number; +} diff --git a/src/middleware.ts b/src/middleware.ts index 0bcde0c1d3161..0fb5ff3c227ac 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -9,6 +9,9 @@ const isDeveloperDocs = process.env.DEVELOPER_DOCS_; export const config = { // learn more: https://nextjs.org/docs/pages/building-your-application/routing/middleware#matcher matcher: [ + // Match MCP routes for trailing slash handling + '/mcp', + '/mcp/', // Match all request paths except for the ones starting with: // - api (API routes) // - _next/static (static files) @@ -18,8 +21,33 @@ export const config = { ], }; +// don't send Permanent Redirects (301) in dev mode - it gets cached for "localhost" by the browser +const redirectStatusCode = process.env.NODE_ENV === 'development' ? 302 : 301; + // This function can be marked `async` if using `await` inside export function middleware(request: NextRequest) { + const {pathname} = request.nextUrl; + + // Handle MCP routes - rewrite trailing slash version to non-trailing + if (pathname === '/mcp/') { + const url = request.nextUrl.clone(); + url.pathname = '/mcp'; + return NextResponse.rewrite(url); + } + + // Skip trailing slash redirect for MCP and API routes + if (pathname.startsWith('/mcp') || pathname.startsWith('/api')) { + return NextResponse.next(); + } + + // Add trailing slash redirect for docs pages (since skipTrailingSlashRedirect is enabled) + if (!pathname.endsWith('/') && !pathname.includes('.')) { + return NextResponse.redirect( + new URL(`${pathname}/`, request.url), + redirectStatusCode + ); + } + // First, handle canonical URL redirects for deprecated paths const canonicalRedirect = handleRedirects(request); if (canonicalRedirect) { @@ -30,9 +58,6 @@ export function middleware(request: NextRequest) { return handleAIClientRedirect(request); } -// don't send Permanent Redirects (301) in dev mode - it gets cached for "localhost" by the browser -const redirectStatusCode = process.env.NODE_ENV === 'development' ? 302 : 301; - /** * Detects if the user agent belongs to an AI/LLM tool or development environment * that would benefit from markdown format From 3c1610fdf39ec931d7a12ffdc852e834054a9fc5 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Sat, 31 Jan 2026 17:05:46 -0500 Subject: [PATCH 3/5] fix(mcp): Use iterative HTML tag stripping to prevent incomplete sanitization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes GitHub Advanced Security alert for incomplete multi-character sanitization. The regex `/<[^>]*>/g` can leave partial tags when input contains nested angle brackets (e.g., `<