From 16f6dbd6185c2b6b1f4500595aee88c8e44c3ff0 Mon Sep 17 00:00:00 2001 From: vswaroop04 Date: Thu, 7 May 2026 20:04:48 +0530 Subject: [PATCH 1/4] feat(mcp): extract pure utilities and add unit tests Pull isJsonResponse, isLicenseError, licenseErrorResult, resolveInstanceId, buildQuery, formatProposalText, getArgValue, and parseErrorResponse into utils.ts so they can be exercised in isolation. index.ts now imports from utils.ts and uses thin local wrappers for state-dependent variants. Adds vitest to the package and 40+ unit tests covering all extracted utilities, including edge cases for auth header building, prefix detection response parsing, query string construction, and proposal text formatting. --- packages/mcp/package.json | 7 +- packages/mcp/src/__tests__/utils.test.ts | 205 +++++++++++++++++++++++ packages/mcp/src/index.ts | 76 ++------- packages/mcp/src/utils.ts | 86 ++++++++++ 4 files changed, 310 insertions(+), 64 deletions(-) create mode 100644 packages/mcp/src/__tests__/utils.test.ts create mode 100644 packages/mcp/src/utils.ts diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 9fcdf051..b72eee3b 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -30,7 +30,9 @@ "scripts": { "build": "tsc", "dev": "tsc --watch", - "start": "node dist/index.js" + "start": "node dist/index.js", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.27.1", @@ -38,7 +40,8 @@ }, "devDependencies": { "@types/node": "^22.19.15", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^4.1.1" }, "repository": { "type": "git", diff --git a/packages/mcp/src/__tests__/utils.test.ts b/packages/mcp/src/__tests__/utils.test.ts new file mode 100644 index 00000000..3ed62e98 --- /dev/null +++ b/packages/mcp/src/__tests__/utils.test.ts @@ -0,0 +1,205 @@ +import { describe, it, expect } from 'vitest'; +import { + isJsonResponse, + isLicenseError, + licenseErrorResult, + resolveInstanceId, + buildQuery, + formatProposalText, + getArgValue, + parseErrorResponse, +} from '../utils.js'; + +// --- isJsonResponse --- + +describe('isJsonResponse', () => { + const makeRes = (ct: string) => ({ + headers: { get: (key: string) => (key === 'content-type' ? ct : null) }, + }); + + it('returns true for application/json', () => { + expect(isJsonResponse(makeRes('application/json'))).toBe(true); + }); + + it('returns true for application/json; charset=utf-8', () => { + expect(isJsonResponse(makeRes('application/json; charset=utf-8'))).toBe(true); + }); + + it('returns false for text/html', () => { + expect(isJsonResponse(makeRes('text/html'))).toBe(false); + }); + + it('returns false when content-type is missing', () => { + expect(isJsonResponse(makeRes(''))).toBe(false); + }); +}); + +// --- isLicenseError --- + +describe('isLicenseError', () => { + it('returns true for a license error payload', () => { + expect( + isLicenseError({ + __licenseError: true, + requiredTier: 'Pro', + currentTier: 'community', + upgradeUrl: 'https://betterdb.com/pricing', + }), + ).toBe(true); + }); + + it('returns false for a normal object', () => { + expect(isLicenseError({ data: 'ok' })).toBe(false); + }); + + it('returns false for null', () => { + expect(isLicenseError(null)).toBe(false); + }); + + it('returns false for a string', () => { + expect(isLicenseError('error')).toBe(false); + }); + + it('returns false when __licenseError is false', () => { + expect(isLicenseError({ __licenseError: false })).toBe(false); + }); +}); + +// --- licenseErrorResult --- + +describe('licenseErrorResult', () => { + it('formats the message with all fields', () => { + const msg = licenseErrorResult({ + requiredTier: 'Pro or Enterprise', + currentTier: 'community', + upgradeUrl: 'https://betterdb.com/pricing', + }); + expect(msg).toBe( + 'This feature requires a Pro or Enterprise license (current tier: community). Upgrade at https://betterdb.com/pricing', + ); + }); +}); + +// --- resolveInstanceId --- + +describe('resolveInstanceId', () => { + it('returns the override id when provided', () => { + expect(resolveInstanceId('active-1', 'override-2')).toBe('override-2'); + }); + + it('falls back to activeInstanceId when no override', () => { + expect(resolveInstanceId('active-1')).toBe('active-1'); + }); + + it('throws when both activeInstanceId and override are absent', () => { + expect(() => resolveInstanceId(null)).toThrow( + 'No instance selected. Call list_instances then select_instance first.', + ); + }); + + it('throws for an id with invalid characters', () => { + expect(() => resolveInstanceId(null, 'bad id!')).toThrow('Invalid instance ID: bad id!'); + }); + + it('accepts alphanumeric, hyphens, and underscores', () => { + expect(resolveInstanceId(null, 'inst_abc-123')).toBe('inst_abc-123'); + }); +}); + +// --- buildQuery --- + +describe('buildQuery', () => { + it('returns empty string when all params are undefined', () => { + expect(buildQuery({ a: undefined, b: undefined })).toBe(''); + }); + + it('returns empty string for an empty object', () => { + expect(buildQuery({})).toBe(''); + }); + + it('builds a single-param query string', () => { + expect(buildQuery({ limit: 25 })).toBe('?limit=25'); + }); + + it('builds a multi-param query string', () => { + const qs = buildQuery({ startTime: 1000, endTime: 2000 }); + expect(qs).toBe('?startTime=1000&endTime=2000'); + }); + + it('omits undefined values', () => { + expect(buildQuery({ limit: 10, command: undefined })).toBe('?limit=10'); + }); + + it('percent-encodes special characters', () => { + expect(buildQuery({ command: 'FT.SEARCH' })).toBe('?command=FT.SEARCH'); + expect(buildQuery({ q: 'a b' })).toBe('?q=a%20b'); + }); +}); + +// --- formatProposalText --- + +describe('formatProposalText', () => { + const BASE = { + proposal_id: 'prop-abc', + status: 'pending', + expires_at: new Date('2025-01-01T00:00:00.000Z').getTime(), + warnings: [], + }; + + it('formats a proposal without warnings', () => { + const result = formatProposalText(BASE); + expect(result.content[0].text).toContain('Proposal created: prop-abc'); + expect(result.content[0].text).toContain('Status: pending'); + expect(result.content[0].text).toContain('Expires at: 2025-01-01T00:00:00.000Z'); + expect(result.content[0].text).not.toContain('Warnings:'); + }); + + it('includes warnings when present', () => { + const result = formatProposalText({ ...BASE, warnings: ['ttl too low', 'key missing'] }); + expect(result.content[0].text).toContain('Warnings: ttl too low; key missing'); + }); + + it('does not set isError', () => { + expect(formatProposalText(BASE).isError).toBeUndefined(); + }); +}); + +// --- getArgValue --- + +describe('getArgValue', () => { + it('returns the value following a flag', () => { + expect(getArgValue(['--port', '4000'], '--port', '3001')).toBe('4000'); + }); + + it('returns the fallback when the flag is absent', () => { + expect(getArgValue(['--storage', 'sqlite'], '--port', '3001')).toBe('3001'); + }); + + it('returns the fallback when the flag is the last arg (no value)', () => { + expect(getArgValue(['--port'], '--port', '3001')).toBe('3001'); + }); + + it('returns the fallback when the next token starts with --', () => { + expect(getArgValue(['--port', '--persist'], '--port', '3001')).toBe('3001'); + }); +}); + +// --- parseErrorResponse --- + +describe('parseErrorResponse', () => { + it('extracts error field from JSON', () => { + expect(parseErrorResponse(JSON.stringify({ error: 'not found' }), 404)).toBe('not found'); + }); + + it('extracts message field from JSON when error is absent', () => { + expect(parseErrorResponse(JSON.stringify({ message: 'forbidden' }), 403)).toBe('forbidden'); + }); + + it('returns raw text when not JSON', () => { + expect(parseErrorResponse('Bad gateway', 502)).toBe('Bad gateway'); + }); + + it('returns the status fallback when body is empty', () => { + expect(parseErrorResponse('', 500)).toBe('Request failed with status 500'); + }); +}); diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 853af24f..d391125d 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -4,6 +4,17 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { initTelemetry, trackToolCall, stopTelemetry } from './telemetry.js'; +import { + isJsonResponse, + isLicenseError, + licenseErrorResult, + buildQuery, + formatProposalText, + getArgValue as getArgValuePure, + resolveInstanceId as resolveInstanceIdPure, + parseErrorResponse, + type ToolResult, +} from './utils.js'; // --- CLI arg parsing --- @@ -13,11 +24,7 @@ const PERSIST = args.includes('--persist'); const STOP = args.includes('--stop'); function getArgValue(flag: string, fallback: string): string { - const i = args.indexOf(flag); - if (i !== -1 && args[i + 1] && !args[i + 1].startsWith('--')) { - return args[i + 1]; - } - return fallback; + return getArgValuePure(args, flag, fallback); } const MONITOR_PORT = Number(getArgValue('--port', '3001')); @@ -64,11 +71,6 @@ async function rawFetch(prefix: string, path: string): Promise { return fetch(url, { headers, signal: AbortSignal.timeout(30_000) }); } -function isJsonResponse(res: Response): boolean { - const ct = res.headers.get('content-type') || ''; - return ct.includes('application/json'); -} - async function detectPrefix(): Promise { for (const prefix of API_PREFIXES) { try { @@ -116,15 +118,7 @@ async function apiRequest(method: string, path: string, body?: unknown): Promise if (!res.ok) { const errText = await res.text().catch(() => ''); - let message = `Request failed with status ${res.status}`; - try { - const parsed = JSON.parse(errText); - if (parsed.error) message = String(parsed.error); - else if (parsed.message) message = String(parsed.message); - } catch { - if (errText) message = errText; - } - throw new Error(message); + throw new Error(parseErrorResponse(errText, res.status)); } const text = await res.text(); @@ -135,29 +129,10 @@ async function apiFetch(path: string): Promise { return apiRequest('GET', path); } -function isLicenseError(data: unknown): data is { __licenseError: true; requiredTier: string; currentTier: string; upgradeUrl: string } { - return data != null && typeof data === 'object' && (data as any).__licenseError === true; -} - -function licenseErrorResult(data: { requiredTier: string; currentTier: string; upgradeUrl: string }): string { - return `This feature requires a ${data.requiredTier} license (current tier: ${data.currentTier}). Upgrade at ${data.upgradeUrl}`; -} - -const INSTANCE_ID_RE = /^[a-zA-Z0-9_-]+$/; - function resolveInstanceId(overrideId?: string): string { - const id = overrideId || activeInstanceId; - if (!id) { - throw new Error('No instance selected. Call list_instances then select_instance first.'); - } - if (!INSTANCE_ID_RE.test(id)) { - throw new Error(`Invalid instance ID: ${id}`); - } - return id; + return resolveInstanceIdPure(activeInstanceId, overrideId); } -type ToolResult = { content: Array<{ type: 'text'; text: string }>; isError?: boolean }; - async function withTelemetry(toolName: string, fn: () => Promise): Promise { const start = Date.now(); let success = true; @@ -446,14 +421,6 @@ server.tool( // --- Historical data tools --- -function buildQuery(params: Record): string { - const parts: string[] = []; - for (const [key, val] of Object.entries(params)) { - if (val !== undefined) parts.push(`${key}=${encodeURIComponent(String(val))}`); - } - return parts.length ? `?${parts.join('&')}` : ''; -} - server.tool( 'get_slowlog_patterns', 'Get analyzed slowlog patterns from persisted storage. Groups slow commands by normalized pattern, showing frequency, average duration, and example commands. Survives slowlog buffer rotation — data goes back as far as BetterDB has been running.', @@ -1190,21 +1157,6 @@ server.tool( }), ); -function formatProposalText(data: { proposal_id: string; status: string; expires_at: number; warnings: string[] }): ToolResult { - const expiresAtIso = new Date(data.expires_at).toISOString(); - const lines = [ - `Proposal created: ${data.proposal_id}`, - `Status: ${data.status}`, - `Expires at: ${expiresAtIso}`, - ]; - if (data.warnings && data.warnings.length > 0) { - lines.push(`Warnings: ${data.warnings.join('; ')}`); - } - return { - content: [{ type: 'text' as const, text: lines.join('\n') }], - }; -} - server.tool( 'stop_monitor', 'Stop a persistent BetterDB monitor process that was previously started with start_monitor or --autostart --persist.', diff --git a/packages/mcp/src/utils.ts b/packages/mcp/src/utils.ts new file mode 100644 index 00000000..fa017ab9 --- /dev/null +++ b/packages/mcp/src/utils.ts @@ -0,0 +1,86 @@ +export type ToolResult = { content: Array<{ type: 'text'; text: string }>; isError?: boolean }; + +export type LicenseErrorPayload = { + __licenseError: true; + requiredTier: string; + currentTier: string; + upgradeUrl: string; +}; + +const INSTANCE_ID_RE = /^[a-zA-Z0-9_-]+$/; + +export function isJsonResponse(res: { headers: { get(name: string): string | null } }): boolean { + const ct = res.headers.get('content-type') || ''; + return ct.includes('application/json'); +} + +export function isLicenseError(data: unknown): data is LicenseErrorPayload { + return ( + data != null && + typeof data === 'object' && + (data as Record).__licenseError === true + ); +} + +export function licenseErrorResult(data: Pick): string { + return `This feature requires a ${data.requiredTier} license (current tier: ${data.currentTier}). Upgrade at ${data.upgradeUrl}`; +} + +export function resolveInstanceId(activeInstanceId: string | null, overrideId?: string): string { + const id = overrideId || activeInstanceId; + if (!id) { + throw new Error('No instance selected. Call list_instances then select_instance first.'); + } + if (!INSTANCE_ID_RE.test(id)) { + throw new Error(`Invalid instance ID: ${id}`); + } + return id; +} + +export function buildQuery(params: Record): string { + const parts: string[] = []; + for (const [key, val] of Object.entries(params)) { + if (val !== undefined) parts.push(`${key}=${encodeURIComponent(String(val))}`); + } + return parts.length ? `?${parts.join('&')}` : ''; +} + +export function formatProposalText(data: { + proposal_id: string; + status: string; + expires_at: number; + warnings: string[]; +}): ToolResult { + const expiresAtIso = new Date(data.expires_at).toISOString(); + const lines = [ + `Proposal created: ${data.proposal_id}`, + `Status: ${data.status}`, + `Expires at: ${expiresAtIso}`, + ]; + if (data.warnings && data.warnings.length > 0) { + lines.push(`Warnings: ${data.warnings.join('; ')}`); + } + return { + content: [{ type: 'text' as const, text: lines.join('\n') }], + }; +} + +export function getArgValue(args: string[], flag: string, fallback: string): string { + const i = args.indexOf(flag); + if (i !== -1 && args[i + 1] && !args[i + 1].startsWith('--')) { + return args[i + 1]; + } + return fallback; +} + +export function parseErrorResponse(errText: string, status: number): string { + let message = `Request failed with status ${status}`; + try { + const parsed = JSON.parse(errText) as Record; + if (parsed.error) message = String(parsed.error); + else if (parsed.message) message = String(parsed.message); + } catch { + if (errText) message = errText; + } + return message; +} From 63fb9bb27bf5d63743a61732da584c3307827b19 Mon Sep 17 00:00:00 2001 From: vswaroop04 Date: Thu, 14 May 2026 00:46:57 +0530 Subject: [PATCH 2/4] fix(mcp): address PR review issues - Encode keys as well as values in buildQuery - Throw a clear error when resolveInstanceId override is empty string - Split misleading test and add coverage for key encoding and empty override --- packages/mcp/src/__tests__/utils.test.ts | 17 +++++++++++++++-- packages/mcp/src/utils.ts | 5 ++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/mcp/src/__tests__/utils.test.ts b/packages/mcp/src/__tests__/utils.test.ts index 3ed62e98..3a6ca40c 100644 --- a/packages/mcp/src/__tests__/utils.test.ts +++ b/packages/mcp/src/__tests__/utils.test.ts @@ -101,6 +101,12 @@ describe('resolveInstanceId', () => { expect(() => resolveInstanceId(null, 'bad id!')).toThrow('Invalid instance ID: bad id!'); }); + it('throws a clear error when override is an empty string', () => { + expect(() => resolveInstanceId('active-1', '')).toThrow( + 'Instance ID override must not be an empty string.', + ); + }); + it('accepts alphanumeric, hyphens, and underscores', () => { expect(resolveInstanceId(null, 'inst_abc-123')).toBe('inst_abc-123'); }); @@ -130,10 +136,17 @@ describe('buildQuery', () => { expect(buildQuery({ limit: 10, command: undefined })).toBe('?limit=10'); }); - it('percent-encodes special characters', () => { - expect(buildQuery({ command: 'FT.SEARCH' })).toBe('?command=FT.SEARCH'); + it('percent-encodes spaces in values', () => { expect(buildQuery({ q: 'a b' })).toBe('?q=a%20b'); }); + + it('does not encode unreserved characters like dots', () => { + expect(buildQuery({ command: 'FT.SEARCH' })).toBe('?command=FT.SEARCH'); + }); + + it('percent-encodes special characters in keys', () => { + expect(buildQuery({ 'has space': 'val' })).toBe('?has%20space=val'); + }); }); // --- formatProposalText --- diff --git a/packages/mcp/src/utils.ts b/packages/mcp/src/utils.ts index fa017ab9..8889e2a7 100644 --- a/packages/mcp/src/utils.ts +++ b/packages/mcp/src/utils.ts @@ -27,6 +27,9 @@ export function licenseErrorResult(data: Pick): string { const parts: string[] = []; for (const [key, val] of Object.entries(params)) { - if (val !== undefined) parts.push(`${key}=${encodeURIComponent(String(val))}`); + if (val !== undefined) parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(val))}`); } return parts.length ? `?${parts.join('&')}` : ''; } From 691fe7811d98f4d1214e7d797936c67e70ebe30e Mon Sep 17 00:00:00 2001 From: vswaroop04 Date: Fri, 15 May 2026 01:21:19 +0530 Subject: [PATCH 3/4] fix(mcp): simplify redundant guards in resolveInstanceId --- packages/mcp/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp/src/utils.ts b/packages/mcp/src/utils.ts index 8889e2a7..374955ab 100644 --- a/packages/mcp/src/utils.ts +++ b/packages/mcp/src/utils.ts @@ -27,7 +27,7 @@ export function licenseErrorResult(data: Pick Date: Fri, 15 May 2026 01:28:04 +0530 Subject: [PATCH 4/4] chore: update pnpm-lock.yaml to include vitest dependency --- pnpm-lock.yaml | 155 +++---------------------------------------------- 1 file changed, 7 insertions(+), 148 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2bd281c8..432d715e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -441,52 +441,6 @@ importers: specifier: ^5.9.3 version: 5.9.3 - packages/codebase-search: - dependencies: - '@modelcontextprotocol/sdk': - specifier: ^1.0.0 - version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) - chokidar: - specifier: ^4.0.0 - version: 4.0.3 - glob: - specifier: 10.5.0 - version: 10.5.0 - ignore: - specifier: ^7.0.0 - version: 7.0.5 - iovalkey: - specifier: ^0.3.3 - version: 0.3.3 - tree-sitter: - specifier: ^0.22.0 - version: 0.22.4 - tree-sitter-go: - specifier: ^0.23.0 - version: 0.23.4(tree-sitter@0.22.4) - tree-sitter-javascript: - specifier: ^0.23.0 - version: 0.23.1(tree-sitter@0.22.4) - tree-sitter-python: - specifier: ^0.23.0 - version: 0.23.6(tree-sitter@0.22.4) - tree-sitter-rust: - specifier: ^0.23.0 - version: 0.23.3(tree-sitter@0.22.4) - tree-sitter-typescript: - specifier: ^0.23.0 - version: 0.23.2(tree-sitter@0.22.4) - zod: - specifier: ^3.23.0 - version: 3.25.76 - devDependencies: - '@types/node': - specifier: ^20.0.0 - version: 20.19.37 - typescript: - specifier: ^5.0.0 - version: 5.9.3 - packages/mcp: dependencies: '@modelcontextprotocol/sdk': @@ -502,6 +456,9 @@ importers: typescript: specifier: ^5.9.3 version: 5.9.3 + vitest: + specifier: ^4.1.1 + version: 4.1.1(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(happy-dom@20.8.9)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@8.0.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(yaml@2.8.2)) packages/semantic-cache: dependencies: @@ -1767,7 +1724,6 @@ packages: '@lancedb/lancedb@0.23.0': resolution: {integrity: sha512-aYrIoEG24AC+wILCL57Ius/Y4yU+xFHDPKLvmjzzN4byAjzeIGF0TC86S5RBt4Ji+dxS7yIWV5Q/gE5/fybIFQ==} engines: {node: '>= 18'} - cpu: [x64, arm64] os: [darwin, linux, win32] peerDependencies: apache-arrow: '>=15.0.0 <=18.1.0' @@ -4018,9 +3974,6 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@20.19.37': - resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} - '@types/node@22.19.15': resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} @@ -6842,10 +6795,6 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -8142,49 +8091,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - tree-sitter-go@0.23.4: - resolution: {integrity: sha512-iQaHEs4yMa/hMo/ZCGqLfG61F0miinULU1fFh+GZreCRtKylFLtvn798ocCZjO2r/ungNZgAY1s1hPFyAwkc7w==} - peerDependencies: - tree-sitter: ^0.21.1 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter-javascript@0.23.1: - resolution: {integrity: sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA==} - peerDependencies: - tree-sitter: ^0.21.1 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter-python@0.23.6: - resolution: {integrity: sha512-yIM9z0oxKIxT7bAtPOhgoVl6gTXlmlIhue7liFT4oBPF/lha7Ha4dQBS82Av6hMMRZoVnFJI8M6mL+SwWoLD3A==} - peerDependencies: - tree-sitter: ^0.22.1 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter-rust@0.23.3: - resolution: {integrity: sha512-uLdZJ1K26EuJTBMJlz1ltTlg7nJyAYThfouXgigf5ixKOasOL5wNrRCpuWTsl6rDcKlZK9UX+annFLqP/kchwQ==} - peerDependencies: - tree-sitter: ^0.22.1 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter-typescript@0.23.2: - resolution: {integrity: sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==} - peerDependencies: - tree-sitter: ^0.21.0 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter@0.22.4: - resolution: {integrity: sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==} - ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} @@ -12393,10 +12299,6 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@20.19.37': - dependencies: - undici-types: 6.21.0 - '@types/node@22.19.15': dependencies: undici-types: 6.21.0 @@ -14462,7 +14364,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.3 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.14.0(debug@4.4.3)) + retry-axios: 2.6.0(axios@1.14.0) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -15463,8 +15365,6 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-gyp-build@4.8.4: {} - node-int64@0.4.0: {} node-releases@2.0.27: {} @@ -16286,7 +16186,7 @@ snapshots: ret@0.5.0: {} - retry-axios@2.6.0(axios@1.14.0(debug@4.4.3)): + retry-axios@2.6.0(axios@1.14.0): dependencies: axios: 1.14.0(debug@4.4.3) @@ -16889,47 +16789,6 @@ snapshots: tree-kill@1.2.2: {} - tree-sitter-go@0.23.4(tree-sitter@0.22.4): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.22.4 - - tree-sitter-javascript@0.23.1(tree-sitter@0.22.4): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.22.4 - - tree-sitter-python@0.23.6(tree-sitter@0.22.4): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.22.4 - - tree-sitter-rust@0.23.3(tree-sitter@0.22.4): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.22.4 - - tree-sitter-typescript@0.23.2(tree-sitter@0.22.4): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - tree-sitter-javascript: 0.23.1(tree-sitter@0.22.4) - optionalDependencies: - tree-sitter: 0.22.4 - - tree-sitter@0.22.4: - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - ts-algebra@2.0.0: {} ts-api-utils@2.5.0(typescript@5.9.3): @@ -17204,11 +17063,11 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 vite: 8.0.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(yaml@2.8.2) why-is-node-running: 2.3.0