@j0hanz/filesystem-mcp 2.9.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -413,6 +413,7 @@ export async function searchContent(directory, pattern, options, pathGuard) {
413
413
  baseNameMatch: true,
414
414
  includeHidden: Boolean(options.includeHidden),
415
415
  skipIgnored: Boolean(options.skipIgnored),
416
+ pathGuard,
416
417
  ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),
417
418
  ...(options.signal ? { signal: options.signal } : {}),
418
419
  ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),
@@ -528,6 +529,7 @@ export async function searchFiles(directory, pattern, options, pathGuard) {
528
529
  baseNameMatch: true,
529
530
  includeHidden: Boolean(options.includeHidden),
530
531
  skipIgnored: Boolean(options.skipIgnored),
532
+ pathGuard,
531
533
  ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),
532
534
  ...(options.signal ? { signal: options.signal } : {}),
533
535
  ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, dirname } from 'node:path';\n\nimport type { RE2ExecArray } from '@adguard/re2-wasm';\nimport { RE2 } from '@adguard/re2-wasm';\n\nimport { processEntriesConcurrently, resolveStopReason } from './concurrency.ts';\nimport { ErrorCode, FsError, isFsError } from './errors.ts';\nimport { GuardedFileSystem } from './fs.ts';\nimport { globEntries } from './glob.ts';\nimport type { PathGuard } from './path.ts';\nimport { escapeRegExp, getMaxTextFileSize, MIB, PARALLEL_CONCURRENCY } from './util.ts';\n\n/**\n * A fresh, flat copy of `s`. V8 keeps a substring of a long string as a slice\n * that references its parent, so a kept line would otherwise pin the whole\n * file's text for as long as the match list lives (60 s in the page store).\n */\nconst own = (s: string): string => Buffer.from(s, 'utf8').toString('utf8');\n\ninterface SearchResult {\n file: string;\n line: number;\n /** 0-indexed column of the first occurrence on the line. */\n column: number;\n content: string;\n matchCount: number;\n /** Up to `context` lines either side of the match; absent when context is 0. */\n before?: string[];\n after?: string[];\n}\n\nexport type Regex = RE2;\n\n/** Occurrence-counting bound, so one pathological line cannot spin forever. */\nconst MAX_MATCHES_PER_LINE = 100_000;\n\n/**\n * re2-wasm copies the whole input on every exec, so draining a dense pattern is\n * O(matches x input) and fully synchronous: the loop pins and the tool's\n * AbortSignal.timeout (a timer) can never fire. The returned `tick` yields a\n * macrotask every ~10ms and reports whether `signal` has aborted.\n */\nexport function createYielder(signal: AbortSignal | undefined): () => Promise<boolean> {\n let last = Date.now();\n return async () => {\n if (Date.now() - last > 10) {\n await new Promise((resolve) => {\n setImmediate(resolve);\n });\n last = Date.now();\n }\n return signal?.aborted ?? false;\n };\n}\n\n/**\n * Largest input one RE2 call may scan as a single string. The input is copied\n * into the same fixed 16 MB wasm heap as the patterns; about 10 MB of input\n * aborts the module. 4 MiB leaves room for the copy and the pattern set.\n */\nexport const RE2_MAX_INPUT_BYTES = 4 * MIB;\n\n/** The chunk `FileHandle#readFile` reads in, and checks its signal between. */\nconst READ_CHUNK_BYTES = 512 * 1024;\n\n/** One line's matches: how many, and where the first starts (UTF-16 units). */\ninterface LineHit {\n count: number;\n column: number;\n}\n\n/**\n * A literal needle matched in native code, as RE2 would match it. RE2 copies\n * its whole input into the wasm heap on every call, and for a literal that\n * copy is most of what a search costs: once per file to rule it out, then once\n * per line of every file it could not rule out.\n */\ninterface LiteralMatcher {\n /** True when this file holds a character RE2 folds onto one of the needle's letters. */\n needsRe2(buffer: Buffer): boolean;\n /** False only when the raw bytes cannot hold a match. */\n mayMatch(buffer: Buffer): boolean;\n matchLine(line: string): LineHit | undefined;\n}\n\n/** The only non-ASCII characters RE2's case folding maps onto ASCII letters. */\nconst KELVIN_SIGN_UTF8 = Buffer.from('\\u212A', 'utf8'); // folds onto k\nconst LONG_S_UTF8 = Buffer.from('\\u017F', 'utf8'); // folds onto s\nconst ASCII_UPPER_RE = /[A-Z]+/gu;\n\n/** Non-overlapping occurrences, left to right, as a global RE2 exec walks them. */\nfunction countOccurrences(line: string, needle: string): LineHit | undefined {\n const column = line.indexOf(needle);\n if (column === -1) return undefined;\n let count = 0;\n let at = column;\n while (at !== -1 && count < MAX_MATCHES_PER_LINE) {\n count++;\n at = line.indexOf(needle, at + needle.length);\n }\n return { count, column };\n}\n\n/** `undefined` when RE2 must judge the needle: these shortcuts are exact only for the needles they take. */\nfunction createLiteralMatcher(needle: string, caseSensitive: boolean): LiteralMatcher | undefined {\n // The file is decoded with U+FFFD for every invalid byte, and a lone\n // surrogate reaches RE2 as U+FFFD, so a needle holding either can match\n // decoded text that the raw bytes, or indexOf, do not.\n if (needle.includes('\\uFFFD') || Buffer.from(needle, 'utf8').toString('utf8') !== needle) {\n return undefined;\n }\n if (caseSensitive) {\n const bytes = Buffer.from(needle, 'utf8');\n return {\n needsRe2: () => false,\n mayMatch: (buffer) => buffer.includes(bytes),\n matchLine: (line) => countOccurrences(line, needle),\n };\n }\n // Every non-ASCII character takes more than one UTF-8 byte per UTF-16 unit.\n if (Buffer.byteLength(needle, 'utf8') !== needle.length) return undefined;\n const lower = needle.toLowerCase();\n const folds = [\n ...(lower.includes('k') ? [KELVIN_SIGN_UTF8] : []),\n ...(lower.includes('s') ? [LONG_S_UTF8] : []),\n ];\n return {\n needsRe2: (buffer) => folds.some((fold) => buffer.includes(fold)),\n // Latin-1 decoding keeps one char per byte, and lowercasing it maps A-Z\n // onto a-z and every other byte onto a non-ASCII char, so an ASCII needle\n // is found exactly where the bytes spell it in either case.\n mayMatch: (buffer) => buffer.toString('latin1').toLowerCase().includes(lower),\n // ASCII letters only: String#toLowerCase turns U+0130 into two code units,\n // which shifts every later column and spells an `i` RE2 does not match.\n matchLine: (line) =>\n countOccurrences(\n line.replace(ASCII_UPPER_RE, (upper) => upper.toLowerCase()),\n lower,\n ),\n };\n}\n\nconst BUFFER_ANCHOR_RE = /\\\\[AzZ]/u;\n/** `(?i)`, `(?-m)`, `(?s:…)`: inline flags can undo the `m` the prefilter relies on. */\nconst INLINE_FLAGS_RE = /\\(\\?[a-zA-Z-]+[:)]/u;\nconst CR = 13;\n\n/**\n * Compile a pattern on RE2 rather than on V8's irregexp.\n *\n * Patterns arrive from the MCP client, so a backtracking engine would let one\n * request pin the event loop with no way out: an abort signal cannot preempt a\n * synchronous `exec`, and on stdio that wedges the whole server. RE2 matches in\n * time linear in the input and cannot backtrack at all, so the hazard is gone\n * rather than bounded.\n *\n * RE2 rejects the constructs the tool schema documents as unsupported —\n * lookahead, lookbehind, backreferences — with its own {@link SyntaxError},\n * which the tool layer turns into a normal tool error. It always matches in\n * Unicode mode and requires the `u` flag to say so.\n *\n * Every compiled pattern owns memory in re2-wasm's fixed 16 MB heap, which\n * `ALLOW_MEMORY_GROWTH` is off for. re2-wasm never frees it and a\n * FinalizationRegistry does not keep up (V8 sees no pressure from the wasm\n * heap), so exhaustion is an emscripten `abort()` that kills regex search for\n * the rest of the process. Every caller MUST pass the result to\n * {@link freeRegex} when it is done with it.\n */\nexport function compileRegex(\n pattern: string,\n options: { caseSensitive?: boolean; multiline?: boolean } = {},\n): Regex {\n const flags = `g${options.multiline ? 'm' : ''}${options.caseSensitive ? '' : 'i'}u`;\n try {\n return new RE2(pattern, flags);\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error;\n throw new SyntaxError(\n `${error.message} — lookahead, lookbehind, and backreferences are not supported.`,\n { cause: error },\n );\n }\n}\n\n/**\n * Release a compiled pattern's wasm memory. re2-wasm exposes no disposal of its\n * own, so this reaches the embind handle it holds privately; a version that\n * renames the field degrades to the pre-existing leak rather than throwing.\n * Idempotent — a second call on an already-freed handle is swallowed. Using a\n * {@link Regex} after freeing it is undefined behaviour in the wasm heap, so\n * free only in a `finally` that owns the compile.\n */\nexport function freeRegex(regex: Regex | undefined): void {\n const handle = (regex as unknown as { wrapper?: { delete?: () => void } } | undefined)?.wrapper;\n try {\n handle?.delete?.();\n } catch {\n // already deleted, or a re2-wasm build without embind disposal\n }\n}\n\nexport interface RegexMatch {\n /** The matched text. */\n text: string;\n /** Capture groups, 1-indexed in the pattern, 0-indexed here. */\n groups: (string | undefined)[];\n /** Named capture groups, when the pattern declares any. */\n named: Record<string, string> | undefined;\n /** Start offset in UTF-16 code units — usable with `String.prototype.slice`. */\n start: number;\n /** End offset in UTF-16 code units. */\n end: number;\n}\n\n/** Code-point length of `text`, i.e. its UTF-16 length minus its surrogate pairs. */\nconst codePointLength = (text: string): number =>\n // eslint-disable-next-line @typescript-eslint/no-misused-spread -- code points are the intended unit (re2-wasm offsets count code points); grapheme segmentation would change the count, not preserve it.\n [...text].length;\n\n/**\n * Iterate a global pattern's non-overlapping matches with offsets JS can use.\n *\n * re2-wasm reports `index` and `lastIndex` in **code points**, while every JS\n * string operation indexes in UTF-16 code units. The two agree only until the\n * first astral character (most emoji), past which every offset is short by one\n * per surrogate pair — slicing on the raw index cuts into the middle of\n * neighbouring text. Its `lastIndex` bookkeeping is worse still: it adds the\n * match's UTF-16 length to a code-point index, so an astral character inside a\n * match can skip the next one. This is the single place that corrects both; no\n * caller should read `match.index` or `regex.lastIndex` directly.\n *\n * Zero-length matches (e.g. `a*`) advance by one code point so they cannot loop\n * forever. The regex is global and shared across calls, so `lastIndex` is reset\n * on entry.\n */\nexport function* execMatches(regex: Regex, text: string): Generator<RegexMatch, undefined> {\n regex.lastIndex = 0;\n // Cursor into `text`, carried forward across matches: RE2 reports them in\n // ascending order, so the walk is O(text) in total rather than per match.\n let cursorCp = 0;\n let cursorU16 = 0;\n let match: RE2ExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n while (cursorCp < match.index && cursorU16 < text.length) {\n const isPair =\n (text.charCodeAt(cursorU16) & 0xfc00) === 0xd800 &&\n (text.charCodeAt(cursorU16 + 1) & 0xfc00) === 0xdc00;\n cursorU16 += isPair ? 2 : 1;\n cursorCp++;\n }\n const matched = match[0] ?? '';\n const start = cursorU16;\n yield {\n text: matched,\n groups: match.slice(1),\n named: match.groups,\n start,\n end: start + matched.length,\n };\n // Own the advance rather than trusting the wrapper's mixed-unit arithmetic.\n regex.lastIndex = match.index + (matched.length === 0 ? 1 : codePointLength(matched));\n }\n}\n\n/**\n * Find non-overlapping occurrences of a global regex in a single line, reporting\n * the first match's column alongside the count.\n */\nasync function findLineMatches(\n regex: Regex,\n line: string,\n tick: () => Promise<boolean>,\n): Promise<LineHit | undefined> {\n let count = 0;\n let column = -1;\n // Awaiting mid-drain is safe with a shared regex: execMatches re-writes\n // lastIndex from its own match before every exec, with no await inside.\n for (const match of execMatches(regex, line)) {\n if (column === -1) column = match.start;\n count++;\n if (count >= MAX_MATCHES_PER_LINE || (await tick())) break;\n }\n return count > 0 ? { count, column } : undefined;\n}\n\nexport interface SearchContentOptions {\n caseSensitive?: boolean;\n isRegex?: boolean;\n maxResults?: number;\n filePattern?: string;\n skipIgnored?: boolean;\n includeHidden?: boolean;\n maxDepth?: number;\n /**\n * Search exactly this file (absolute, already guard-validated) instead of\n * walking `directory` with `filePattern`. Set when the caller named a file:\n * its name must not be interpreted as a glob.\n */\n explicitFile?: string;\n /** Lines of context to carry either side of each match; 0 (default) carries none. */\n context?: number;\n signal?: AbortSignal;\n}\n\nexport interface SearchContentOutcome {\n basePath: string;\n matches: SearchResult[];\n summary: {\n /**\n * Matching *lines*, one per entry in `matches` — not pattern occurrences.\n * A line with three occurrences counts once here and reports 3 in its own\n * `SearchResult.matchCount`.\n */\n matchingLines: number;\n filesScanned: number;\n filesMatched: number;\n truncated: boolean;\n /** Files the guard rejected or that could not be stat'd. */\n skippedInaccessible: number;\n /** Files skipped unread because they exceed maxFileSize. */\n skippedTooLarge: number;\n /** Files skipped because they contain a NUL byte (binary). */\n skippedBinary: number;\n /**\n * `StoppedReason` narrowed to the stops these scans can produce: both\n * resolve the stop through `resolveStopReason` (concurrency.ts) — never\n * `maxFiles`, which belongs to `replace_text`'s per-file cap.\n */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}\n\ntype FileScan =\n | { kind: 'sensitive' }\n | { kind: 'inaccessible' }\n | { kind: 'tooLarge' }\n | { kind: 'binary' }\n | { kind: 'scanned'; matches: SearchResult[] };\n\ninterface ScanContext {\n fs: GuardedFileSystem;\n signal: AbortSignal | undefined;\n maxFileSize: number;\n maxResults: number;\n regex: Regex;\n prefilter: Regex;\n /** Set for a literal needle it can match; RE2 then judges only the files it hands back. */\n literal: LiteralMatcher | undefined;\n source: string;\n context: number;\n}\n\nasync function scanFile(entryPath: string, scanCtx: ScanContext): Promise<FileScan> {\n let buffer: Buffer;\n try {\n // open() validates, refuses a swapped final symlink (O_NOFOLLOW) and\n // re-checks the descriptor's real path (H4), so these bytes are the\n // validated file's, not whatever the name points at by now.\n await using handle = await scanCtx.fs.open(entryPath);\n const { size } = await handle.stat();\n if (size > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n // Read the size fstat reported: handle.readFile would fstat again and then\n // read once more to find EOF, two extra round trips on every file a walk\n // touches. A file that grows meanwhile is searched as it was at the fstat.\n // A size of 0 is also what procfs and other virtual files report while\n // holding text, so those still read to EOF.\n if (size === 0) {\n buffer = await handle.readFile({ signal: scanCtx.signal });\n if (buffer.length > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n } else {\n buffer = Buffer.allocUnsafe(size);\n let filled = 0;\n while (filled < size) {\n // In readFile's own chunks, with its check of the signal before each,\n // so a deadline on a slow filesystem lands as soon as it did there.\n scanCtx.signal?.throwIfAborted();\n const length = Math.min(size - filled, READ_CHUNK_BYTES);\n const { bytesRead } = await handle.read(buffer, filled, length, filled);\n if (bytesRead === 0) break;\n filled += bytesRead;\n }\n buffer = buffer.subarray(0, filled);\n }\n } catch (error) {\n if (scanCtx.signal?.aborted) throw error;\n if (\n scanCtx.fs.pathGuard.isSensitive(entryPath) ||\n (isFsError(error) && error.message.startsWith('Sensitive file'))\n ) {\n return { kind: 'sensitive' };\n }\n return { kind: 'inaccessible' };\n }\n\n // A NUL byte marks a binary file, as it does for `read` and `grep -I`.\n // Non-UTF-8 text is still searched: a lossy decode only affects the\n // bytes a pattern could not have matched anyway.\n if (buffer.includes(0)) return { kind: 'binary' };\n\n const literal = scanCtx.literal?.needsRe2(buffer) ? undefined : scanCtx.literal;\n if (literal && !literal.mayMatch(buffer)) return { kind: 'scanned', matches: [] };\n const content = buffer.toString('utf-8');\n if (!literal && canPrefilter(scanCtx.source, buffer)) {\n scanCtx.prefilter.lastIndex = 0;\n if (scanCtx.prefilter.exec(content) === null) return { kind: 'scanned', matches: [] };\n }\n\n const lines = content.split(/\\r?\\n/u);\n // A trailing newline splits into a phantom empty last element, and an\n // empty file splits into one empty element: neither is a line the\n // file has, for matching or for context.\n const lineCount =\n content.length === 0 ? 0 : content.endsWith('\\n') ? lines.length - 1 : lines.length;\n // A single line past RE2_MAX_INPUT_BYTES OOMs the wasm module for the rest of\n // the process; report the file tooLarge instead. Only a buffer over the bound\n // can hold such a line, so ordinary files skip the per-line measure, and a\n // literal match never hands a line to RE2.\n const checkLineSize = !literal && buffer.length > RE2_MAX_INPUT_BYTES;\n const matches: SearchResult[] = [];\n const tick = createYielder(scanCtx.signal);\n for (let i = 0; i < lineCount; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n if (checkLineSize && Buffer.byteLength(line, 'utf8') > RE2_MAX_INPUT_BYTES) {\n return { kind: 'tooLarge' };\n }\n // Yield between lines too: millions of short lines never yield per-match.\n if (await tick()) break;\n // One scan per line: the helper resets lastIndex itself, so it\n // doubles as the \"does this line match\" test.\n const found = literal\n ? literal.matchLine(line)\n : await findLineMatches(scanCtx.regex, line, tick);\n if (found) {\n matches.push({\n file: entryPath,\n line: i + 1,\n column: found.column,\n content: own(line),\n matchCount: found.count,\n ...(scanCtx.context > 0\n ? {\n before: lines.slice(Math.max(0, i - scanCtx.context), i).map(own),\n after: lines.slice(i + 1, Math.min(lineCount, i + 1 + scanCtx.context)).map(own),\n }\n : {}),\n });\n if (matches.length >= scanCtx.maxResults) break;\n }\n }\n return { kind: 'scanned', matches };\n}\n\nexport async function searchContent(\n directory: string,\n pattern: string,\n options: SearchContentOptions,\n pathGuard: PathGuard,\n): Promise<SearchContentOutcome> {\n const source = options.isRegex ? pattern || '' : escapeRegExp(pattern || '');\n // Compiles live inside the try so a failing second compile does not leak the\n // first's wasm memory; freeRegex is null-safe.\n let regex: Regex | undefined;\n let prefilter: Regex | undefined;\n try {\n regex = compileRegex(source, {\n caseSensitive: Boolean(options.caseSensitive),\n });\n // Whole-buffer test: `m` makes ^/$ line-relative so a per-line hit is always a\n // buffer hit (the converse can be false — a false positive just falls through\n // to the per-line scan). \\A/\\z/\\Z are buffer-relative even under `m`, inline\n // flag groups can undo `m`, and $ cannot see past a \\r, so those inputs skip\n // the prefilter (see canPrefilter).\n prefilter = compileRegex(source, {\n caseSensitive: Boolean(options.caseSensitive),\n multiline: true,\n });\n const matches: SearchResult[] = [];\n const maxResults = options.maxResults ?? 100;\n const maxFileSize = getMaxTextFileSize();\n const context = options.context ?? 0;\n const ceiling = pathGuard.allowedRootContaining(directory);\n\n const entries = options.explicitFile\n ? [{ path: options.explicitFile }]\n : globEntries({\n cwd: directory,\n pattern: options.filePattern ?? '**/*',\n // Same rule as replace_text, so a search with the same glob previews the\n // files a replace would touch: a slash-free glob matches at any depth.\n baseNameMatch: true,\n includeHidden: Boolean(options.includeHidden),\n skipIgnored: Boolean(options.skipIgnored),\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),\n suppressErrors: true,\n });\n\n let filesScanned = 0;\n let filesMatched = 0;\n let matchingLines = 0;\n let skippedTooLarge = 0;\n let skippedBinary = 0;\n const scanCtx: ScanContext = {\n fs: new GuardedFileSystem(pathGuard),\n signal: options.signal,\n maxFileSize,\n maxResults,\n regex,\n prefilter,\n literal: options.isRegex\n ? undefined\n : createLiteralMatcher(pattern, Boolean(options.caseSensitive)),\n source,\n context,\n };\n const perFile = new Map<number, FileScan>();\n let dispatched = 0;\n let collected = 0;\n const stopped = await processEntriesConcurrently(entries, {\n signal: options.signal,\n concurrency: PARALLEL_CONCURRENCY,\n shouldStop: () => collected >= maxResults,\n onEntry: () => {\n dispatched++;\n },\n onError: () => undefined,\n runEntry: async (entryPath) => {\n const index = dispatched - 1;\n const scan = await scanFile(entryPath, scanCtx);\n perFile.set(index, scan);\n if (scan.kind === 'scanned') collected += scan.matches.length;\n },\n });\n\n let skippedInaccessible = 0;\n for (let i = 0; i < dispatched; i++) {\n const scan = perFile.get(i);\n if (!scan) continue;\n switch (scan.kind) {\n case 'sensitive':\n break;\n case 'inaccessible':\n skippedInaccessible++;\n break;\n case 'tooLarge':\n skippedTooLarge++;\n break;\n case 'binary':\n filesScanned++;\n skippedBinary++;\n break;\n case 'scanned':\n filesScanned++;\n if (scan.matches.length > 0) filesMatched++;\n for (const match of scan.matches) {\n if (matches.length >= maxResults) break;\n matches.push(match);\n matchingLines++;\n }\n break;\n }\n }\n\n const stoppedReason = resolveStopReason(\n matches.length >= maxResults,\n stopped === 'timeout' || Boolean(options.signal?.aborted),\n );\n\n return {\n basePath: directory,\n matches,\n summary: {\n matchingLines,\n filesScanned,\n filesMatched,\n truncated: stoppedReason !== undefined,\n skippedInaccessible,\n skippedTooLarge,\n skippedBinary,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n } finally {\n freeRegex(prefilter);\n freeRegex(regex);\n }\n}\n\n/** Whether one whole-buffer RE2 test is a sound \"cannot match\" check for this file. */\nfunction canPrefilter(source: string, buffer: Buffer): boolean {\n if (buffer.length > RE2_MAX_INPUT_BYTES) return false;\n if (BUFFER_ANCHOR_RE.test(source)) return false;\n if (INLINE_FLAGS_RE.test(source)) return false;\n if (source.includes('$') && buffer.includes(CR)) return false;\n return true;\n}\n\nexport async function searchFiles(\n directory: string,\n pattern: string,\n options: {\n maxResults?: number;\n includeHidden?: boolean;\n sortBy?: 'name' | 'path';\n skipIgnored?: boolean;\n maxDepth?: number;\n signal?: AbortSignal;\n },\n pathGuard: PathGuard,\n): Promise<{\n basePath: string;\n results: { path: string }[];\n summary: {\n matched: number;\n filesScanned: number;\n truncated: boolean;\n skippedInaccessible: number;\n /** Narrowed like SearchContentOutcome's — scans never `maxFiles`. */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}> {\n const maxResults = options.maxResults ?? 100;\n const ceiling = pathGuard.allowedRootContaining(directory);\n const entries = globEntries({\n cwd: directory,\n pattern,\n baseNameMatch: true,\n includeHidden: Boolean(options.includeHidden),\n skipIgnored: Boolean(options.skipIgnored),\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),\n suppressErrors: true,\n });\n const results: { path: string }[] = [];\n let filesScanned = 0;\n let skippedInaccessible = 0;\n const perFile = new Map<number, { path: string } | 'inaccessible' | 'sensitive'>();\n let dispatched = 0;\n let collected = 0;\n const stopped = await processEntriesConcurrently(entries, {\n signal: options.signal,\n concurrency: PARALLEL_CONCURRENCY,\n shouldStop: () => collected >= maxResults,\n onEntry: () => {\n dispatched++;\n },\n onError: () => undefined,\n runEntry: async (entryPath) => {\n const index = dispatched - 1;\n try {\n await pathGuard.validateExistingPath(entryPath);\n perFile.set(index, { path: entryPath });\n collected++;\n } catch (error) {\n if (\n pathGuard.isSensitive(entryPath) ||\n (isFsError(error) && error.message.startsWith('Sensitive file'))\n ) {\n perFile.set(index, 'sensitive');\n } else {\n perFile.set(index, 'inaccessible');\n }\n }\n },\n });\n\n for (let i = 0; i < dispatched; i++) {\n const item = perFile.get(i);\n if (!item || item === 'sensitive') continue;\n if (item === 'inaccessible') {\n skippedInaccessible++;\n continue;\n }\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push(item);\n }\n\n // Sorting — only name / path are supported; size / modified were removed\n // (the glob never collected stats, so they were always undefined).\n if (options.sortBy === 'name') {\n results.sort((a, b) => basename(a.path).localeCompare(basename(b.path)));\n } else {\n results.sort((a, b) => a.path.localeCompare(b.path));\n }\n\n // Same precedence as above: the result cap wins over a same-iteration abort.\n const stoppedReason = resolveStopReason(\n results.length >= maxResults,\n stopped === 'timeout' || Boolean(options.signal?.aborted),\n );\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: stoppedReason !== undefined,\n skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n\n/** Explicit files bypass glob filtering; their parent is the directory walked. */\nexport async function resolveSearchScope(\n path: string | undefined,\n fs: GuardedFileSystem,\n signal?: AbortSignal,\n): Promise<{ basePath: string; explicitFile?: string }> {\n const requested = fs.pathGuard.resolvePathOrRoot(path);\n // One resolution, one stat: validateExistingDirectory would redo both.\n const { stats, validPath: resolved } = await fs.stat(requested, { signal });\n if (!stats.isFile()) {\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);\n }\n return { basePath: resolved };\n }\n return {\n basePath: dirname(resolved),\n // The name is NOT turned into a glob: `[slug].tsx` would read as a\n // character class. searchContent scans exactly this file instead, and\n // hidden/ignored filtering is moot for a file the caller named.\n explicitFile: resolved,\n };\n}\n"]}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, dirname } from 'node:path';\n\nimport type { RE2ExecArray } from '@adguard/re2-wasm';\nimport { RE2 } from '@adguard/re2-wasm';\n\nimport { processEntriesConcurrently, resolveStopReason } from './concurrency.ts';\nimport { ErrorCode, FsError, isFsError } from './errors.ts';\nimport { GuardedFileSystem } from './fs.ts';\nimport { globEntries } from './glob.ts';\nimport type { PathGuard } from './path.ts';\nimport { escapeRegExp, getMaxTextFileSize, MIB, PARALLEL_CONCURRENCY } from './util.ts';\n\n/**\n * A fresh, flat copy of `s`. V8 keeps a substring of a long string as a slice\n * that references its parent, so a kept line would otherwise pin the whole\n * file's text for as long as the match list lives (60 s in the page store).\n */\nconst own = (s: string): string => Buffer.from(s, 'utf8').toString('utf8');\n\ninterface SearchResult {\n file: string;\n line: number;\n /** 0-indexed column of the first occurrence on the line. */\n column: number;\n content: string;\n matchCount: number;\n /** Up to `context` lines either side of the match; absent when context is 0. */\n before?: string[];\n after?: string[];\n}\n\nexport type Regex = RE2;\n\n/** Occurrence-counting bound, so one pathological line cannot spin forever. */\nconst MAX_MATCHES_PER_LINE = 100_000;\n\n/**\n * re2-wasm copies the whole input on every exec, so draining a dense pattern is\n * O(matches x input) and fully synchronous: the loop pins and the tool's\n * AbortSignal.timeout (a timer) can never fire. The returned `tick` yields a\n * macrotask every ~10ms and reports whether `signal` has aborted.\n */\nexport function createYielder(signal: AbortSignal | undefined): () => Promise<boolean> {\n let last = Date.now();\n return async () => {\n if (Date.now() - last > 10) {\n await new Promise((resolve) => {\n setImmediate(resolve);\n });\n last = Date.now();\n }\n return signal?.aborted ?? false;\n };\n}\n\n/**\n * Largest input one RE2 call may scan as a single string. The input is copied\n * into the same fixed 16 MB wasm heap as the patterns; about 10 MB of input\n * aborts the module. 4 MiB leaves room for the copy and the pattern set.\n */\nexport const RE2_MAX_INPUT_BYTES = 4 * MIB;\n\n/** The chunk `FileHandle#readFile` reads in, and checks its signal between. */\nconst READ_CHUNK_BYTES = 512 * 1024;\n\n/** One line's matches: how many, and where the first starts (UTF-16 units). */\ninterface LineHit {\n count: number;\n column: number;\n}\n\n/**\n * A literal needle matched in native code, as RE2 would match it. RE2 copies\n * its whole input into the wasm heap on every call, and for a literal that\n * copy is most of what a search costs: once per file to rule it out, then once\n * per line of every file it could not rule out.\n */\ninterface LiteralMatcher {\n /** True when this file holds a character RE2 folds onto one of the needle's letters. */\n needsRe2(buffer: Buffer): boolean;\n /** False only when the raw bytes cannot hold a match. */\n mayMatch(buffer: Buffer): boolean;\n matchLine(line: string): LineHit | undefined;\n}\n\n/** The only non-ASCII characters RE2's case folding maps onto ASCII letters. */\nconst KELVIN_SIGN_UTF8 = Buffer.from('\\u212A', 'utf8'); // folds onto k\nconst LONG_S_UTF8 = Buffer.from('\\u017F', 'utf8'); // folds onto s\nconst ASCII_UPPER_RE = /[A-Z]+/gu;\n\n/** Non-overlapping occurrences, left to right, as a global RE2 exec walks them. */\nfunction countOccurrences(line: string, needle: string): LineHit | undefined {\n const column = line.indexOf(needle);\n if (column === -1) return undefined;\n let count = 0;\n let at = column;\n while (at !== -1 && count < MAX_MATCHES_PER_LINE) {\n count++;\n at = line.indexOf(needle, at + needle.length);\n }\n return { count, column };\n}\n\n/** `undefined` when RE2 must judge the needle: these shortcuts are exact only for the needles they take. */\nfunction createLiteralMatcher(needle: string, caseSensitive: boolean): LiteralMatcher | undefined {\n // The file is decoded with U+FFFD for every invalid byte, and a lone\n // surrogate reaches RE2 as U+FFFD, so a needle holding either can match\n // decoded text that the raw bytes, or indexOf, do not.\n if (needle.includes('\\uFFFD') || Buffer.from(needle, 'utf8').toString('utf8') !== needle) {\n return undefined;\n }\n if (caseSensitive) {\n const bytes = Buffer.from(needle, 'utf8');\n return {\n needsRe2: () => false,\n mayMatch: (buffer) => buffer.includes(bytes),\n matchLine: (line) => countOccurrences(line, needle),\n };\n }\n // Every non-ASCII character takes more than one UTF-8 byte per UTF-16 unit.\n if (Buffer.byteLength(needle, 'utf8') !== needle.length) return undefined;\n const lower = needle.toLowerCase();\n const folds = [\n ...(lower.includes('k') ? [KELVIN_SIGN_UTF8] : []),\n ...(lower.includes('s') ? [LONG_S_UTF8] : []),\n ];\n return {\n needsRe2: (buffer) => folds.some((fold) => buffer.includes(fold)),\n // Latin-1 decoding keeps one char per byte, and lowercasing it maps A-Z\n // onto a-z and every other byte onto a non-ASCII char, so an ASCII needle\n // is found exactly where the bytes spell it in either case.\n mayMatch: (buffer) => buffer.toString('latin1').toLowerCase().includes(lower),\n // ASCII letters only: String#toLowerCase turns U+0130 into two code units,\n // which shifts every later column and spells an `i` RE2 does not match.\n matchLine: (line) =>\n countOccurrences(\n line.replace(ASCII_UPPER_RE, (upper) => upper.toLowerCase()),\n lower,\n ),\n };\n}\n\nconst BUFFER_ANCHOR_RE = /\\\\[AzZ]/u;\n/** `(?i)`, `(?-m)`, `(?s:…)`: inline flags can undo the `m` the prefilter relies on. */\nconst INLINE_FLAGS_RE = /\\(\\?[a-zA-Z-]+[:)]/u;\nconst CR = 13;\n\n/**\n * Compile a pattern on RE2 rather than on V8's irregexp.\n *\n * Patterns arrive from the MCP client, so a backtracking engine would let one\n * request pin the event loop with no way out: an abort signal cannot preempt a\n * synchronous `exec`, and on stdio that wedges the whole server. RE2 matches in\n * time linear in the input and cannot backtrack at all, so the hazard is gone\n * rather than bounded.\n *\n * RE2 rejects the constructs the tool schema documents as unsupported —\n * lookahead, lookbehind, backreferences — with its own {@link SyntaxError},\n * which the tool layer turns into a normal tool error. It always matches in\n * Unicode mode and requires the `u` flag to say so.\n *\n * Every compiled pattern owns memory in re2-wasm's fixed 16 MB heap, which\n * `ALLOW_MEMORY_GROWTH` is off for. re2-wasm never frees it and a\n * FinalizationRegistry does not keep up (V8 sees no pressure from the wasm\n * heap), so exhaustion is an emscripten `abort()` that kills regex search for\n * the rest of the process. Every caller MUST pass the result to\n * {@link freeRegex} when it is done with it.\n */\nexport function compileRegex(\n pattern: string,\n options: { caseSensitive?: boolean; multiline?: boolean } = {},\n): Regex {\n const flags = `g${options.multiline ? 'm' : ''}${options.caseSensitive ? '' : 'i'}u`;\n try {\n return new RE2(pattern, flags);\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error;\n throw new SyntaxError(\n `${error.message} — lookahead, lookbehind, and backreferences are not supported.`,\n { cause: error },\n );\n }\n}\n\n/**\n * Release a compiled pattern's wasm memory. re2-wasm exposes no disposal of its\n * own, so this reaches the embind handle it holds privately; a version that\n * renames the field degrades to the pre-existing leak rather than throwing.\n * Idempotent — a second call on an already-freed handle is swallowed. Using a\n * {@link Regex} after freeing it is undefined behaviour in the wasm heap, so\n * free only in a `finally` that owns the compile.\n */\nexport function freeRegex(regex: Regex | undefined): void {\n const handle = (regex as unknown as { wrapper?: { delete?: () => void } } | undefined)?.wrapper;\n try {\n handle?.delete?.();\n } catch {\n // already deleted, or a re2-wasm build without embind disposal\n }\n}\n\nexport interface RegexMatch {\n /** The matched text. */\n text: string;\n /** Capture groups, 1-indexed in the pattern, 0-indexed here. */\n groups: (string | undefined)[];\n /** Named capture groups, when the pattern declares any. */\n named: Record<string, string> | undefined;\n /** Start offset in UTF-16 code units — usable with `String.prototype.slice`. */\n start: number;\n /** End offset in UTF-16 code units. */\n end: number;\n}\n\n/** Code-point length of `text`, i.e. its UTF-16 length minus its surrogate pairs. */\nconst codePointLength = (text: string): number =>\n // eslint-disable-next-line @typescript-eslint/no-misused-spread -- code points are the intended unit (re2-wasm offsets count code points); grapheme segmentation would change the count, not preserve it.\n [...text].length;\n\n/**\n * Iterate a global pattern's non-overlapping matches with offsets JS can use.\n *\n * re2-wasm reports `index` and `lastIndex` in **code points**, while every JS\n * string operation indexes in UTF-16 code units. The two agree only until the\n * first astral character (most emoji), past which every offset is short by one\n * per surrogate pair — slicing on the raw index cuts into the middle of\n * neighbouring text. Its `lastIndex` bookkeeping is worse still: it adds the\n * match's UTF-16 length to a code-point index, so an astral character inside a\n * match can skip the next one. This is the single place that corrects both; no\n * caller should read `match.index` or `regex.lastIndex` directly.\n *\n * Zero-length matches (e.g. `a*`) advance by one code point so they cannot loop\n * forever. The regex is global and shared across calls, so `lastIndex` is reset\n * on entry.\n */\nexport function* execMatches(regex: Regex, text: string): Generator<RegexMatch, undefined> {\n regex.lastIndex = 0;\n // Cursor into `text`, carried forward across matches: RE2 reports them in\n // ascending order, so the walk is O(text) in total rather than per match.\n let cursorCp = 0;\n let cursorU16 = 0;\n let match: RE2ExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n while (cursorCp < match.index && cursorU16 < text.length) {\n const isPair =\n (text.charCodeAt(cursorU16) & 0xfc00) === 0xd800 &&\n (text.charCodeAt(cursorU16 + 1) & 0xfc00) === 0xdc00;\n cursorU16 += isPair ? 2 : 1;\n cursorCp++;\n }\n const matched = match[0] ?? '';\n const start = cursorU16;\n yield {\n text: matched,\n groups: match.slice(1),\n named: match.groups,\n start,\n end: start + matched.length,\n };\n // Own the advance rather than trusting the wrapper's mixed-unit arithmetic.\n regex.lastIndex = match.index + (matched.length === 0 ? 1 : codePointLength(matched));\n }\n}\n\n/**\n * Find non-overlapping occurrences of a global regex in a single line, reporting\n * the first match's column alongside the count.\n */\nasync function findLineMatches(\n regex: Regex,\n line: string,\n tick: () => Promise<boolean>,\n): Promise<LineHit | undefined> {\n let count = 0;\n let column = -1;\n // Awaiting mid-drain is safe with a shared regex: execMatches re-writes\n // lastIndex from its own match before every exec, with no await inside.\n for (const match of execMatches(regex, line)) {\n if (column === -1) column = match.start;\n count++;\n if (count >= MAX_MATCHES_PER_LINE || (await tick())) break;\n }\n return count > 0 ? { count, column } : undefined;\n}\n\nexport interface SearchContentOptions {\n caseSensitive?: boolean;\n isRegex?: boolean;\n maxResults?: number;\n filePattern?: string;\n skipIgnored?: boolean;\n includeHidden?: boolean;\n maxDepth?: number;\n /**\n * Search exactly this file (absolute, already guard-validated) instead of\n * walking `directory` with `filePattern`. Set when the caller named a file:\n * its name must not be interpreted as a glob.\n */\n explicitFile?: string;\n /** Lines of context to carry either side of each match; 0 (default) carries none. */\n context?: number;\n signal?: AbortSignal;\n}\n\nexport interface SearchContentOutcome {\n basePath: string;\n matches: SearchResult[];\n summary: {\n /**\n * Matching *lines*, one per entry in `matches` — not pattern occurrences.\n * A line with three occurrences counts once here and reports 3 in its own\n * `SearchResult.matchCount`.\n */\n matchingLines: number;\n filesScanned: number;\n filesMatched: number;\n truncated: boolean;\n /** Files the guard rejected or that could not be stat'd. */\n skippedInaccessible: number;\n /** Files skipped unread because they exceed maxFileSize. */\n skippedTooLarge: number;\n /** Files skipped because they contain a NUL byte (binary). */\n skippedBinary: number;\n /**\n * `StoppedReason` narrowed to the stops these scans can produce: both\n * resolve the stop through `resolveStopReason` (concurrency.ts) — never\n * `maxFiles`, which belongs to `replace_text`'s per-file cap.\n */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}\n\ntype FileScan =\n | { kind: 'sensitive' }\n | { kind: 'inaccessible' }\n | { kind: 'tooLarge' }\n | { kind: 'binary' }\n | { kind: 'scanned'; matches: SearchResult[] };\n\ninterface ScanContext {\n fs: GuardedFileSystem;\n signal: AbortSignal | undefined;\n maxFileSize: number;\n maxResults: number;\n regex: Regex;\n prefilter: Regex;\n /** Set for a literal needle it can match; RE2 then judges only the files it hands back. */\n literal: LiteralMatcher | undefined;\n source: string;\n context: number;\n}\n\nasync function scanFile(entryPath: string, scanCtx: ScanContext): Promise<FileScan> {\n let buffer: Buffer;\n try {\n // open() validates, refuses a swapped final symlink (O_NOFOLLOW) and\n // re-checks the descriptor's real path (H4), so these bytes are the\n // validated file's, not whatever the name points at by now.\n await using handle = await scanCtx.fs.open(entryPath);\n const { size } = await handle.stat();\n if (size > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n // Read the size fstat reported: handle.readFile would fstat again and then\n // read once more to find EOF, two extra round trips on every file a walk\n // touches. A file that grows meanwhile is searched as it was at the fstat.\n // A size of 0 is also what procfs and other virtual files report while\n // holding text, so those still read to EOF.\n if (size === 0) {\n buffer = await handle.readFile({ signal: scanCtx.signal });\n if (buffer.length > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n } else {\n buffer = Buffer.allocUnsafe(size);\n let filled = 0;\n while (filled < size) {\n // In readFile's own chunks, with its check of the signal before each,\n // so a deadline on a slow filesystem lands as soon as it did there.\n scanCtx.signal?.throwIfAborted();\n const length = Math.min(size - filled, READ_CHUNK_BYTES);\n const { bytesRead } = await handle.read(buffer, filled, length, filled);\n if (bytesRead === 0) break;\n filled += bytesRead;\n }\n buffer = buffer.subarray(0, filled);\n }\n } catch (error) {\n if (scanCtx.signal?.aborted) throw error;\n if (\n scanCtx.fs.pathGuard.isSensitive(entryPath) ||\n (isFsError(error) && error.message.startsWith('Sensitive file'))\n ) {\n return { kind: 'sensitive' };\n }\n return { kind: 'inaccessible' };\n }\n\n // A NUL byte marks a binary file, as it does for `read` and `grep -I`.\n // Non-UTF-8 text is still searched: a lossy decode only affects the\n // bytes a pattern could not have matched anyway.\n if (buffer.includes(0)) return { kind: 'binary' };\n\n const literal = scanCtx.literal?.needsRe2(buffer) ? undefined : scanCtx.literal;\n if (literal && !literal.mayMatch(buffer)) return { kind: 'scanned', matches: [] };\n const content = buffer.toString('utf-8');\n if (!literal && canPrefilter(scanCtx.source, buffer)) {\n scanCtx.prefilter.lastIndex = 0;\n if (scanCtx.prefilter.exec(content) === null) return { kind: 'scanned', matches: [] };\n }\n\n const lines = content.split(/\\r?\\n/u);\n // A trailing newline splits into a phantom empty last element, and an\n // empty file splits into one empty element: neither is a line the\n // file has, for matching or for context.\n const lineCount =\n content.length === 0 ? 0 : content.endsWith('\\n') ? lines.length - 1 : lines.length;\n // A single line past RE2_MAX_INPUT_BYTES OOMs the wasm module for the rest of\n // the process; report the file tooLarge instead. Only a buffer over the bound\n // can hold such a line, so ordinary files skip the per-line measure, and a\n // literal match never hands a line to RE2.\n const checkLineSize = !literal && buffer.length > RE2_MAX_INPUT_BYTES;\n const matches: SearchResult[] = [];\n const tick = createYielder(scanCtx.signal);\n for (let i = 0; i < lineCount; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n if (checkLineSize && Buffer.byteLength(line, 'utf8') > RE2_MAX_INPUT_BYTES) {\n return { kind: 'tooLarge' };\n }\n // Yield between lines too: millions of short lines never yield per-match.\n if (await tick()) break;\n // One scan per line: the helper resets lastIndex itself, so it\n // doubles as the \"does this line match\" test.\n const found = literal\n ? literal.matchLine(line)\n : await findLineMatches(scanCtx.regex, line, tick);\n if (found) {\n matches.push({\n file: entryPath,\n line: i + 1,\n column: found.column,\n content: own(line),\n matchCount: found.count,\n ...(scanCtx.context > 0\n ? {\n before: lines.slice(Math.max(0, i - scanCtx.context), i).map(own),\n after: lines.slice(i + 1, Math.min(lineCount, i + 1 + scanCtx.context)).map(own),\n }\n : {}),\n });\n if (matches.length >= scanCtx.maxResults) break;\n }\n }\n return { kind: 'scanned', matches };\n}\n\nexport async function searchContent(\n directory: string,\n pattern: string,\n options: SearchContentOptions,\n pathGuard: PathGuard,\n): Promise<SearchContentOutcome> {\n const source = options.isRegex ? pattern || '' : escapeRegExp(pattern || '');\n // Compiles live inside the try so a failing second compile does not leak the\n // first's wasm memory; freeRegex is null-safe.\n let regex: Regex | undefined;\n let prefilter: Regex | undefined;\n try {\n regex = compileRegex(source, {\n caseSensitive: Boolean(options.caseSensitive),\n });\n // Whole-buffer test: `m` makes ^/$ line-relative so a per-line hit is always a\n // buffer hit (the converse can be false — a false positive just falls through\n // to the per-line scan). \\A/\\z/\\Z are buffer-relative even under `m`, inline\n // flag groups can undo `m`, and $ cannot see past a \\r, so those inputs skip\n // the prefilter (see canPrefilter).\n prefilter = compileRegex(source, {\n caseSensitive: Boolean(options.caseSensitive),\n multiline: true,\n });\n const matches: SearchResult[] = [];\n const maxResults = options.maxResults ?? 100;\n const maxFileSize = getMaxTextFileSize();\n const context = options.context ?? 0;\n const ceiling = pathGuard.allowedRootContaining(directory);\n\n const entries = options.explicitFile\n ? [{ path: options.explicitFile }]\n : globEntries({\n cwd: directory,\n pattern: options.filePattern ?? '**/*',\n // Same rule as replace_text, so a search with the same glob previews the\n // files a replace would touch: a slash-free glob matches at any depth.\n baseNameMatch: true,\n includeHidden: Boolean(options.includeHidden),\n skipIgnored: Boolean(options.skipIgnored),\n pathGuard,\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),\n suppressErrors: true,\n });\n\n let filesScanned = 0;\n let filesMatched = 0;\n let matchingLines = 0;\n let skippedTooLarge = 0;\n let skippedBinary = 0;\n const scanCtx: ScanContext = {\n fs: new GuardedFileSystem(pathGuard),\n signal: options.signal,\n maxFileSize,\n maxResults,\n regex,\n prefilter,\n literal: options.isRegex\n ? undefined\n : createLiteralMatcher(pattern, Boolean(options.caseSensitive)),\n source,\n context,\n };\n const perFile = new Map<number, FileScan>();\n let dispatched = 0;\n let collected = 0;\n const stopped = await processEntriesConcurrently(entries, {\n signal: options.signal,\n concurrency: PARALLEL_CONCURRENCY,\n shouldStop: () => collected >= maxResults,\n onEntry: () => {\n dispatched++;\n },\n onError: () => undefined,\n runEntry: async (entryPath) => {\n const index = dispatched - 1;\n const scan = await scanFile(entryPath, scanCtx);\n perFile.set(index, scan);\n if (scan.kind === 'scanned') collected += scan.matches.length;\n },\n });\n\n let skippedInaccessible = 0;\n for (let i = 0; i < dispatched; i++) {\n const scan = perFile.get(i);\n if (!scan) continue;\n switch (scan.kind) {\n case 'sensitive':\n break;\n case 'inaccessible':\n skippedInaccessible++;\n break;\n case 'tooLarge':\n skippedTooLarge++;\n break;\n case 'binary':\n filesScanned++;\n skippedBinary++;\n break;\n case 'scanned':\n filesScanned++;\n if (scan.matches.length > 0) filesMatched++;\n for (const match of scan.matches) {\n if (matches.length >= maxResults) break;\n matches.push(match);\n matchingLines++;\n }\n break;\n }\n }\n\n const stoppedReason = resolveStopReason(\n matches.length >= maxResults,\n stopped === 'timeout' || Boolean(options.signal?.aborted),\n );\n\n return {\n basePath: directory,\n matches,\n summary: {\n matchingLines,\n filesScanned,\n filesMatched,\n truncated: stoppedReason !== undefined,\n skippedInaccessible,\n skippedTooLarge,\n skippedBinary,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n } finally {\n freeRegex(prefilter);\n freeRegex(regex);\n }\n}\n\n/** Whether one whole-buffer RE2 test is a sound \"cannot match\" check for this file. */\nfunction canPrefilter(source: string, buffer: Buffer): boolean {\n if (buffer.length > RE2_MAX_INPUT_BYTES) return false;\n if (BUFFER_ANCHOR_RE.test(source)) return false;\n if (INLINE_FLAGS_RE.test(source)) return false;\n if (source.includes('$') && buffer.includes(CR)) return false;\n return true;\n}\n\nexport async function searchFiles(\n directory: string,\n pattern: string,\n options: {\n maxResults?: number;\n includeHidden?: boolean;\n sortBy?: 'name' | 'path';\n skipIgnored?: boolean;\n maxDepth?: number;\n signal?: AbortSignal;\n },\n pathGuard: PathGuard,\n): Promise<{\n basePath: string;\n results: { path: string }[];\n summary: {\n matched: number;\n filesScanned: number;\n truncated: boolean;\n skippedInaccessible: number;\n /** Narrowed like SearchContentOutcome's — scans never `maxFiles`. */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}> {\n const maxResults = options.maxResults ?? 100;\n const ceiling = pathGuard.allowedRootContaining(directory);\n const entries = globEntries({\n cwd: directory,\n pattern,\n baseNameMatch: true,\n includeHidden: Boolean(options.includeHidden),\n skipIgnored: Boolean(options.skipIgnored),\n pathGuard,\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {}),\n suppressErrors: true,\n });\n const results: { path: string }[] = [];\n let filesScanned = 0;\n let skippedInaccessible = 0;\n const perFile = new Map<number, { path: string } | 'inaccessible' | 'sensitive'>();\n let dispatched = 0;\n let collected = 0;\n const stopped = await processEntriesConcurrently(entries, {\n signal: options.signal,\n concurrency: PARALLEL_CONCURRENCY,\n shouldStop: () => collected >= maxResults,\n onEntry: () => {\n dispatched++;\n },\n onError: () => undefined,\n runEntry: async (entryPath) => {\n const index = dispatched - 1;\n try {\n await pathGuard.validateExistingPath(entryPath);\n perFile.set(index, { path: entryPath });\n collected++;\n } catch (error) {\n if (\n pathGuard.isSensitive(entryPath) ||\n (isFsError(error) && error.message.startsWith('Sensitive file'))\n ) {\n perFile.set(index, 'sensitive');\n } else {\n perFile.set(index, 'inaccessible');\n }\n }\n },\n });\n\n for (let i = 0; i < dispatched; i++) {\n const item = perFile.get(i);\n if (!item || item === 'sensitive') continue;\n if (item === 'inaccessible') {\n skippedInaccessible++;\n continue;\n }\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push(item);\n }\n\n // Sorting — only name / path are supported; size / modified were removed\n // (the glob never collected stats, so they were always undefined).\n if (options.sortBy === 'name') {\n results.sort((a, b) => basename(a.path).localeCompare(basename(b.path)));\n } else {\n results.sort((a, b) => a.path.localeCompare(b.path));\n }\n\n // Same precedence as above: the result cap wins over a same-iteration abort.\n const stoppedReason = resolveStopReason(\n results.length >= maxResults,\n stopped === 'timeout' || Boolean(options.signal?.aborted),\n );\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: stoppedReason !== undefined,\n skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n\n/** Explicit files bypass glob filtering; their parent is the directory walked. */\nexport async function resolveSearchScope(\n path: string | undefined,\n fs: GuardedFileSystem,\n signal?: AbortSignal,\n): Promise<{ basePath: string; explicitFile?: string }> {\n const requested = fs.pathGuard.resolvePathOrRoot(path);\n // One resolution, one stat: validateExistingDirectory would redo both.\n const { stats, validPath: resolved } = await fs.stat(requested, { signal });\n if (!stats.isFile()) {\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);\n }\n return { basePath: resolved };\n }\n return {\n basePath: dirname(resolved),\n // The name is NOT turned into a glob: `[slug].tsx` would read as a\n // character class. searchContent scans exactly this file instead, and\n // hidden/ignored filtering is moot for a file the caller named.\n explicitFile: resolved,\n };\n}\n"]}
@@ -73,6 +73,12 @@ export declare const MAX_STDIO_REQUEST_ID_BYTES = 1024;
73
73
  * overcounts. `toJSON` is followed the way JSON.stringify follows it.
74
74
  */
75
75
  export declare function jsonByteBound(value: unknown): number;
76
+ /**
77
+ * `size` raw bytes' length once base64-encoded: the wire cost of an embedded
78
+ * binary resource. 4/3 rounded up per 3-byte group, standard base64, no line
79
+ * wrapping.
80
+ */
81
+ export declare function base64EncodedBytes(size: number): number;
76
82
  /**
77
83
  * `value`'s JSON-encoded size in bytes when it is over `limit`, else
78
84
  * `undefined`. {@link jsonByteBound} clears most values without encoding them;
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG,QAAa,CAAC;AAE9B,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAER;AA6BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAKhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAQlF;AA6BD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MACgD,CAAC;AAE/F,eAAO,MAAM,oBAAoB,QAAoD,CAAC;AAEtF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AACD,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,QAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,QAAuB,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,0BAA0B,OAAM,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgBpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI/E;AAED,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAQzC;AAED,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG,QAAa,CAAC;AAE9B,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAER;AA6BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAKhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAQlF;AA6BD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MACgD,CAAC;AAE/F,eAAO,MAAM,oBAAoB,QAAoD,CAAC;AAEtF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AACD,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,QAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,QAAuB,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,0BAA0B,OAAM,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgBpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI/E;AAED,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAQzC;AAED,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC"}
package/dist/core/util.js CHANGED
@@ -171,6 +171,14 @@ export function jsonByteBound(value) {
171
171
  }
172
172
  return bytes;
173
173
  }
174
+ /**
175
+ * `size` raw bytes' length once base64-encoded: the wire cost of an embedded
176
+ * binary resource. 4/3 rounded up per 3-byte group, standard base64, no line
177
+ * wrapping.
178
+ */
179
+ export function base64EncodedBytes(size) {
180
+ return 4 * Math.ceil(size / 3);
181
+ }
174
182
  /**
175
183
  * `value`'s JSON-encoded size in bytes when it is over `limit`, else
176
184
  * `undefined`. {@link jsonByteBound} clears most values without encoding them;
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { availableParallelism } from 'node:os';\n\nimport { cli } from './config.ts';\nimport { warnInvalidSetting } from './path-utils.ts';\n\nconst KIB = 1024;\nexport const MIB = 1024 * KIB;\n\nexport function parseEnvInt(\n envVar: string,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n return parseIntSetting(envVar, process.env[envVar], defaultValue, min, max);\n}\n\n/** Validate a raw integer setting (env var or CLI override) with a logged fallback. */\nfunction parseIntSetting(\n name: string,\n value: string | undefined,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n if (!value) {\n return defaultValue;\n }\n\n const trimmed = value.trim();\n const parsed = Number(trimmed);\n if (\n trimmed === '' ||\n Number.isNaN(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < min ||\n parsed > max\n ) {\n warnInvalidSetting(name, value, `${String(min)}-${String(max)}`, defaultValue);\n return defaultValue;\n }\n return parsed;\n}\n\n/**\n * Split a comma-separated config value into trimmed, non-empty entries. Empty\n * entries are dropped so \",\" or \" \" reads as unset rather than as a list of\n * blanks. Every comma-separated env var this server accepts parses through\n * here, so they all agree on what \"configured\" means.\n */\nexport function splitCsvList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/**\n * Parse a boolean env flag: `true`/`1` enable, `false`/`0`/empty disable.\n * Any other value warns once per var (when `name` is given) and reads as\n * false, so a typo like `FS_ALLOW_SENSITIVE=yes` is never silent.\n */\nexport function parseTrueEnvFlag(value: string | undefined, name?: string): boolean {\n if (value === undefined) return false;\n const trimmed = value.trim().toLowerCase();\n if (trimmed === 'true' || trimmed === '1') return true;\n if (name && trimmed !== '' && trimmed !== 'false' && trimmed !== '0') {\n warnInvalidSetting(name, value, '\"true\" or \"1\"', false);\n }\n return false;\n}\n\nconst REGEXP_SYNTAX = /[\\\\^$.*+?()[\\]{}|/]/;\nconst REGEXP_OTHER_PUNCTUATORS = /[,\\-=<>#&!%:;@~'`\"]/;\nconst REGEXP_CONTROL_ESCAPES: Readonly<Record<string, string>> = {\n '\\t': 't',\n '\\n': 'n',\n '\\v': 'v',\n '\\f': 'f',\n '\\r': 'r',\n};\n\nconst hexEscape = (code: number, width: 2 | 4): string =>\n `\\\\${width === 2 ? 'x' : 'u'}${code.toString(16).padStart(width, '0')}`;\n\nfunction encodeForRegExpEscape(ch: string): string {\n if (REGEXP_SYNTAX.test(ch)) return `\\\\${ch}`;\n const control = REGEXP_CONTROL_ESCAPES[ch];\n if (control !== undefined) return `\\\\${control}`;\n const code = ch.codePointAt(0) ?? 0;\n const loneSurrogate = ch.length === 1 && code >= 0xd800 && code <= 0xdfff;\n if (!REGEXP_OTHER_PUNCTUATORS.test(ch) && !/\\s/.test(ch) && !loneSurrogate) return ch;\n if (code <= 0xff) return hexEscape(code, 2);\n return ch\n .split('')\n .map((unit) => hexEscape(unit.charCodeAt(0), 4))\n .join('');\n}\n\n/** The TC39 `RegExp.escape` algorithm, for runtimes that lack it. */\nexport function escapeRegExpFallback(input: string): string {\n let out = '';\n for (const ch of input) {\n out +=\n out === '' && /[0-9A-Za-z]/.test(ch)\n ? hexEscape(ch.charCodeAt(0), 2)\n : encodeForRegExpEscape(ch);\n }\n return out;\n}\n\n/**\n * `RegExp.escape` shipped in Node 24. Older runtimes still load the published\n * package (npm only warns on `engines`), so fall back rather than crash.\n */\nexport const escapeRegExp: (input: string) => string =\n typeof RegExp.escape === 'function' ? (input) => RegExp.escape(input) : escapeRegExpFallback;\n\nexport const PARALLEL_CONCURRENCY = Math.min(Math.max(availableParallelism(), 4), 32);\n\nexport const ROOTS_TIMEOUT_MS = 5000;\n\nexport function getMaxTextFileSize(): number {\n return parseIntSetting(\n 'FS_MAX_FILE_SIZE',\n cli.maxFileSize ?? process.env['FS_MAX_FILE_SIZE'],\n 10 * MIB,\n MIB,\n 100 * MIB,\n );\n}\n\n/**\n * Upper bound, in bytes, on one inbound JSON-RPC message on either transport.\n * Derived from the file-size limit so that a schema-valid `create` — whose\n * combined content is capped at getMaxTextFileSize() (create.ts) in UTF-16\n * units — still fits after JSON encoding. Per unit the schema counted, the\n * wire carries at most three bytes for well-formed text: a BMP character is\n * up to three UTF-8 bytes, an escaped ASCII character (newline, quote,\n * backslash, tab) is two, a surrogate pair is four bytes for two units. Hence\n * 3× the content, plus 1 MiB for the envelope, paths and sibling arguments.\n * Not covered, deliberately: C0 control characters and lone surrogates\n * (JSON-escaped to six bytes each) — the read tools already refuse such\n * content as binary, and a caller sending it only loses its own connection.\n * Past this bound HTTP answers 413 and stdio closes the connection (the\n * SDK's ReadBuffer contract), so it is also the number the instructions\n * resource and --print-config report.\n */\nexport function getMaxInboundMessageBytes(): number {\n return 3 * getMaxTextFileSize() + MIB;\n}\n/** Combined byte budget for one batched `read`. */\nexport const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;\n\n/**\n * Ceiling on JSON-encoded tool and resource results over stdio. The SDK's\n * fixed 10 MiB ReadBuffer applies regardless of `--max-file-size`; escaping,\n * duplicated metadata, and base64 can overflow it even when the source fits.\n * A tool call that changed nothing (read-only, or a dry run) and a resource\n * read return TOO_LARGE instead, and the stdio result store refuses an entry\n * whose read would overflow it; HTTP is unaffected. ~256 KiB covers the\n * JSON-RPC envelope this measure omits, including the bounded request ID.\n * Mutating tools need pre-commit safeguards rather than a post-commit output\n * refusal.\n */\nexport const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;\n\n/**\n * JSON-encoded stdio request IDs and string progress tokens, including\n * quotes/escapes, must fit the envelope reserve: each is echoed into frames.\n */\nexport const MAX_STDIO_REQUEST_ID_BYTES = KIB;\n\n/**\n * An upper bound on `Buffer.byteLength(JSON.stringify(value))` for the plain\n * JSON data tool and resource results are made of, from a walk that reads\n * string lengths but never encodes a string. JSON spends at most six bytes on\n * one UTF-16 code unit (`\\uXXXX` for a control character or a lone surrogate;\n * any other BMP character is at most three UTF-8 bytes, a surrogate pair four\n * for its two units), so a string or key costs at most 6 × length + 2 quotes.\n * A finite number costs its `String()` form, which is what JSON writes, and a\n * non-finite one `null`; `true`, `false` and `null` at most five. Each array\n * element and object member adds one separator, and a member one colon, on top\n * of the two brackets. A member JSON would omit (undefined, a function) only\n * overcounts. `toJSON` is followed the way JSON.stringify follows it.\n */\nexport function jsonByteBound(value: unknown): number {\n if (typeof value === 'string') return 6 * value.length + 2;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value).length : 4;\n if (typeof value !== 'object' || value === null) return 5;\n if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {\n return jsonByteBound((value as { toJSON: () => unknown }).toJSON());\n }\n let bytes = 2;\n if (Array.isArray(value)) {\n for (const item of value as unknown[]) bytes += jsonByteBound(item) + 1;\n return bytes;\n }\n for (const [key, item] of Object.entries(value)) {\n bytes += 6 * key.length + 2 + 1 + jsonByteBound(item) + 1;\n }\n return bytes;\n}\n\n/**\n * `value`'s JSON-encoded size in bytes when it is over `limit`, else\n * `undefined`. {@link jsonByteBound} clears most values without encoding them;\n * only one it cannot clear pays for the exact `JSON.stringify` measure.\n */\nexport function jsonBytesOver(value: unknown, limit: number): number | undefined {\n if (jsonByteBound(value) <= limit) return undefined;\n const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8');\n return bytes > limit ? bytes : undefined;\n}\n\n/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\n/**\n * Deadline for one call of a tool that walks or reads the disk (list, stat,\n * read, find_files, search_text, replace_text). Read per call, so the\n * `--tool-timeout` flag applied by cli.ts after module load still counts.\n */\nexport function getToolTimeoutMs(): number {\n return parseIntSetting(\n 'FS_TOOL_TIMEOUT_MS',\n cli.toolTimeout ?? process.env['FS_TOOL_TIMEOUT_MS'],\n 5000,\n 1000,\n 600_000,\n );\n}\n\nexport const MAX_TREE_DEPTH = 50;\nexport const DEFAULT_TREE_ENTRIES = 1000;\n\nexport const MAX_LIST_ENTRIES = 20000;\n\nexport const MAX_SEARCH_RESULTS = 10000;\nexport const DEFAULT_SEARCH_RESULTS = 100;\nexport const MAX_SEARCH_DEPTH = 100;\n\nexport const DEFAULT_SEARCH_CONTENT_RESULTS = 500;\n\n/**\n * Upper bound on array elements plus object members in one `tools/call`\n * `arguments` payload, counted by the SDK before any schema runs\n * (`McpServerOptions.maxToolInputElements`). Must clear the largest\n * schema-legal call: an `edit` batch of 5 files × 100 edits is 1517 nodes\n * and a 1000-path `read`/`delete` is ~1005, so 4096 is ~2.7× headroom.\n * Raise it in the same change that raises any of those schema caps.\n */\nexport const MAX_TOOL_INPUT_ELEMENTS = 4096;\n"]}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/core/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,GAAG,GAAG,IAAI,CAAC;AACjB,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC;AAE9B,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,YAAoB,EACpB,GAAW,EACX,GAAW;IAEX,OAAO,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,uFAAuF;AACvF,SAAS,eAAe,CACtB,IAAY,EACZ,KAAyB,EACzB,YAAoB,EACpB,GAAW,EACX,GAAW;IAEX,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/B,IACE,OAAO,KAAK,EAAE;QACd,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACpB,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACzB,MAAM,GAAG,GAAG;QACZ,MAAM,GAAG,GAAG,EACZ,CAAC;QACD,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAC/E,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;SACjB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAyB,EAAE,IAAa;IACvE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,IAAI,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;QACrE,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAC5C,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AACvD,MAAM,sBAAsB,GAAqC;IAC/D,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,KAAY,EAAU,EAAE,CACvD,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;AAE1E,SAAS,qBAAqB,CAAC,EAAU;IACvC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,EAAE,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,OAAO,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC;IAC1E,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa;QAAE,OAAO,EAAE,CAAC;IACtF,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5C,OAAO,EAAE;SACN,KAAK,CAAC,EAAE,CAAC;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SAC/C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,GAAG;YACD,GAAG,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAChC,CAAC,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC;AAE/F,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,MAAM,UAAU,kBAAkB;IAChC,OAAO,eAAe,CACpB,kBAAkB,EAClB,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAClD,EAAE,GAAG,GAAG,EACR,GAAG,EACH,GAAG,GAAG,GAAG,CACV,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,CAAC,GAAG,kBAAkB,EAAE,GAAG,GAAG,CAAC;AACxC,CAAC;AACD,mDAAmD;AACnD,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,GAAG,GAAG,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE9D;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IAC1D,IAAI,OAAQ,KAA8B,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACjE,OAAO,aAAa,CAAE,KAAmC,CAAC,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,KAAkB;YAAE,KAAK,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc,EAAE,KAAa;IACzD,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/D,OAAO,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3C,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,CACpB,oBAAoB,EACpB,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EACpD,IAAI,EACJ,IAAI,EACJ,OAAO,CACR,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACxC,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEpC,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC","sourcesContent":["import { availableParallelism } from 'node:os';\n\nimport { cli } from './config.ts';\nimport { warnInvalidSetting } from './path-utils.ts';\n\nconst KIB = 1024;\nexport const MIB = 1024 * KIB;\n\nexport function parseEnvInt(\n envVar: string,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n return parseIntSetting(envVar, process.env[envVar], defaultValue, min, max);\n}\n\n/** Validate a raw integer setting (env var or CLI override) with a logged fallback. */\nfunction parseIntSetting(\n name: string,\n value: string | undefined,\n defaultValue: number,\n min: number,\n max: number,\n): number {\n if (!value) {\n return defaultValue;\n }\n\n const trimmed = value.trim();\n const parsed = Number(trimmed);\n if (\n trimmed === '' ||\n Number.isNaN(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < min ||\n parsed > max\n ) {\n warnInvalidSetting(name, value, `${String(min)}-${String(max)}`, defaultValue);\n return defaultValue;\n }\n return parsed;\n}\n\n/**\n * Split a comma-separated config value into trimmed, non-empty entries. Empty\n * entries are dropped so \",\" or \" \" reads as unset rather than as a list of\n * blanks. Every comma-separated env var this server accepts parses through\n * here, so they all agree on what \"configured\" means.\n */\nexport function splitCsvList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/**\n * Parse a boolean env flag: `true`/`1` enable, `false`/`0`/empty disable.\n * Any other value warns once per var (when `name` is given) and reads as\n * false, so a typo like `FS_ALLOW_SENSITIVE=yes` is never silent.\n */\nexport function parseTrueEnvFlag(value: string | undefined, name?: string): boolean {\n if (value === undefined) return false;\n const trimmed = value.trim().toLowerCase();\n if (trimmed === 'true' || trimmed === '1') return true;\n if (name && trimmed !== '' && trimmed !== 'false' && trimmed !== '0') {\n warnInvalidSetting(name, value, '\"true\" or \"1\"', false);\n }\n return false;\n}\n\nconst REGEXP_SYNTAX = /[\\\\^$.*+?()[\\]{}|/]/;\nconst REGEXP_OTHER_PUNCTUATORS = /[,\\-=<>#&!%:;@~'`\"]/;\nconst REGEXP_CONTROL_ESCAPES: Readonly<Record<string, string>> = {\n '\\t': 't',\n '\\n': 'n',\n '\\v': 'v',\n '\\f': 'f',\n '\\r': 'r',\n};\n\nconst hexEscape = (code: number, width: 2 | 4): string =>\n `\\\\${width === 2 ? 'x' : 'u'}${code.toString(16).padStart(width, '0')}`;\n\nfunction encodeForRegExpEscape(ch: string): string {\n if (REGEXP_SYNTAX.test(ch)) return `\\\\${ch}`;\n const control = REGEXP_CONTROL_ESCAPES[ch];\n if (control !== undefined) return `\\\\${control}`;\n const code = ch.codePointAt(0) ?? 0;\n const loneSurrogate = ch.length === 1 && code >= 0xd800 && code <= 0xdfff;\n if (!REGEXP_OTHER_PUNCTUATORS.test(ch) && !/\\s/.test(ch) && !loneSurrogate) return ch;\n if (code <= 0xff) return hexEscape(code, 2);\n return ch\n .split('')\n .map((unit) => hexEscape(unit.charCodeAt(0), 4))\n .join('');\n}\n\n/** The TC39 `RegExp.escape` algorithm, for runtimes that lack it. */\nexport function escapeRegExpFallback(input: string): string {\n let out = '';\n for (const ch of input) {\n out +=\n out === '' && /[0-9A-Za-z]/.test(ch)\n ? hexEscape(ch.charCodeAt(0), 2)\n : encodeForRegExpEscape(ch);\n }\n return out;\n}\n\n/**\n * `RegExp.escape` shipped in Node 24. Older runtimes still load the published\n * package (npm only warns on `engines`), so fall back rather than crash.\n */\nexport const escapeRegExp: (input: string) => string =\n typeof RegExp.escape === 'function' ? (input) => RegExp.escape(input) : escapeRegExpFallback;\n\nexport const PARALLEL_CONCURRENCY = Math.min(Math.max(availableParallelism(), 4), 32);\n\nexport const ROOTS_TIMEOUT_MS = 5000;\n\nexport function getMaxTextFileSize(): number {\n return parseIntSetting(\n 'FS_MAX_FILE_SIZE',\n cli.maxFileSize ?? process.env['FS_MAX_FILE_SIZE'],\n 10 * MIB,\n MIB,\n 100 * MIB,\n );\n}\n\n/**\n * Upper bound, in bytes, on one inbound JSON-RPC message on either transport.\n * Derived from the file-size limit so that a schema-valid `create` — whose\n * combined content is capped at getMaxTextFileSize() (create.ts) in UTF-16\n * units — still fits after JSON encoding. Per unit the schema counted, the\n * wire carries at most three bytes for well-formed text: a BMP character is\n * up to three UTF-8 bytes, an escaped ASCII character (newline, quote,\n * backslash, tab) is two, a surrogate pair is four bytes for two units. Hence\n * 3× the content, plus 1 MiB for the envelope, paths and sibling arguments.\n * Not covered, deliberately: C0 control characters and lone surrogates\n * (JSON-escaped to six bytes each) — the read tools already refuse such\n * content as binary, and a caller sending it only loses its own connection.\n * Past this bound HTTP answers 413 and stdio closes the connection (the\n * SDK's ReadBuffer contract), so it is also the number the instructions\n * resource and --print-config report.\n */\nexport function getMaxInboundMessageBytes(): number {\n return 3 * getMaxTextFileSize() + MIB;\n}\n/** Combined byte budget for one batched `read`. */\nexport const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;\n\n/**\n * Ceiling on JSON-encoded tool and resource results over stdio. The SDK's\n * fixed 10 MiB ReadBuffer applies regardless of `--max-file-size`; escaping,\n * duplicated metadata, and base64 can overflow it even when the source fits.\n * A tool call that changed nothing (read-only, or a dry run) and a resource\n * read return TOO_LARGE instead, and the stdio result store refuses an entry\n * whose read would overflow it; HTTP is unaffected. ~256 KiB covers the\n * JSON-RPC envelope this measure omits, including the bounded request ID.\n * Mutating tools need pre-commit safeguards rather than a post-commit output\n * refusal.\n */\nexport const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;\n\n/**\n * JSON-encoded stdio request IDs and string progress tokens, including\n * quotes/escapes, must fit the envelope reserve: each is echoed into frames.\n */\nexport const MAX_STDIO_REQUEST_ID_BYTES = KIB;\n\n/**\n * An upper bound on `Buffer.byteLength(JSON.stringify(value))` for the plain\n * JSON data tool and resource results are made of, from a walk that reads\n * string lengths but never encodes a string. JSON spends at most six bytes on\n * one UTF-16 code unit (`\\uXXXX` for a control character or a lone surrogate;\n * any other BMP character is at most three UTF-8 bytes, a surrogate pair four\n * for its two units), so a string or key costs at most 6 × length + 2 quotes.\n * A finite number costs its `String()` form, which is what JSON writes, and a\n * non-finite one `null`; `true`, `false` and `null` at most five. Each array\n * element and object member adds one separator, and a member one colon, on top\n * of the two brackets. A member JSON would omit (undefined, a function) only\n * overcounts. `toJSON` is followed the way JSON.stringify follows it.\n */\nexport function jsonByteBound(value: unknown): number {\n if (typeof value === 'string') return 6 * value.length + 2;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value).length : 4;\n if (typeof value !== 'object' || value === null) return 5;\n if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {\n return jsonByteBound((value as { toJSON: () => unknown }).toJSON());\n }\n let bytes = 2;\n if (Array.isArray(value)) {\n for (const item of value as unknown[]) bytes += jsonByteBound(item) + 1;\n return bytes;\n }\n for (const [key, item] of Object.entries(value)) {\n bytes += 6 * key.length + 2 + 1 + jsonByteBound(item) + 1;\n }\n return bytes;\n}\n\n/**\n * `size` raw bytes' length once base64-encoded: the wire cost of an embedded\n * binary resource. 4/3 rounded up per 3-byte group, standard base64, no line\n * wrapping.\n */\nexport function base64EncodedBytes(size: number): number {\n return 4 * Math.ceil(size / 3);\n}\n\n/**\n * `value`'s JSON-encoded size in bytes when it is over `limit`, else\n * `undefined`. {@link jsonByteBound} clears most values without encoding them;\n * only one it cannot clear pays for the exact `JSON.stringify` measure.\n */\nexport function jsonBytesOver(value: unknown, limit: number): number | undefined {\n if (jsonByteBound(value) <= limit) return undefined;\n const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8');\n return bytes > limit ? bytes : undefined;\n}\n\n/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\n/**\n * Deadline for one call of a tool that walks or reads the disk (list, stat,\n * read, find_files, search_text, replace_text). Read per call, so the\n * `--tool-timeout` flag applied by cli.ts after module load still counts.\n */\nexport function getToolTimeoutMs(): number {\n return parseIntSetting(\n 'FS_TOOL_TIMEOUT_MS',\n cli.toolTimeout ?? process.env['FS_TOOL_TIMEOUT_MS'],\n 5000,\n 1000,\n 600_000,\n );\n}\n\nexport const MAX_TREE_DEPTH = 50;\nexport const DEFAULT_TREE_ENTRIES = 1000;\n\nexport const MAX_LIST_ENTRIES = 20000;\n\nexport const MAX_SEARCH_RESULTS = 10000;\nexport const DEFAULT_SEARCH_RESULTS = 100;\nexport const MAX_SEARCH_DEPTH = 100;\n\nexport const DEFAULT_SEARCH_CONTENT_RESULTS = 500;\n\n/**\n * Upper bound on array elements plus object members in one `tools/call`\n * `arguments` payload, counted by the SDK before any schema runs\n * (`McpServerOptions.maxToolInputElements`). Must clear the largest\n * schema-legal call: an `edit` batch of 5 files × 100 edits is 1517 nodes\n * and a 1000-path `read`/`delete` is ~1005, so 4096 is ~2.7× headroom.\n * Raise it in the same change that raises any of those schema caps.\n */\nexport const MAX_TOOL_INPUT_ELEMENTS = 4096;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
package/dist/resources.js CHANGED
@@ -5,7 +5,7 @@ import { GuardedFileSystem } from "./core/fs.js";
5
5
  import { Logger, voidSend } from "./core/observability.js";
6
6
  import { suggestPaths } from "./core/path-completer.js";
7
7
  import { entryReadResult } from "./core/store.js";
8
- import { getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES } from "./core/util.js";
8
+ import { base64EncodedBytes, getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES, } from "./core/util.js";
9
9
  import { createWatcherRegistry, MAX_WATCHERS, } from "./core/watcher-registry.js";
10
10
  import { buildSectionsRecord, INSTRUCTIONS_SUMMARY, INSTRUCTIONS_URI, renderSections, } from "./instructions.js";
11
11
  /**
@@ -112,7 +112,7 @@ function createFilesystemResource(options) {
112
112
  }
113
113
  const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });
114
114
  if (stdio && readResult.isBinary) {
115
- const base64Bytes = 4 * Math.ceil(readResult.content.length / 3);
115
+ const base64Bytes = base64EncodedBytes(readResult.content.length);
116
116
  if (base64Bytes > MAX_OUTBOUND_RESULT_BYTES)
117
117
  throw tooLargeForStdio(base64Bytes, true);
118
118
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CacheHint,\n CompleteResourceTemplateCallback,\n McpServer,\n ReadResourceResult,\n Resource,\n Role,\n ServerContext,\n ServerNotifier,\n SubscribeRequestParams,\n UnsubscribeRequestParams,\n} from '@modelcontextprotocol/server';\nimport {\n ProtocolError,\n ProtocolErrorCode,\n ResourceNotFoundError,\n ResourceTemplate,\n UriTemplate,\n} from '@modelcontextprotocol/server';\n\nimport type { FsError } from './core/errors.ts';\nimport { ErrorCode, formatUnknownErrorMessage, isFsError } from './core/errors.ts';\nimport {\n decodeFileUriPath,\n encodeFileUriPath,\n extractPath,\n FILESYSTEM_FILE_URI_TEMPLATE,\n} from './core/file-uri.ts';\nimport { GuardedFileSystem } from './core/fs.ts';\nimport { Logger, voidSend } from './core/observability.ts';\nimport { suggestPaths } from './core/path-completer.ts';\nimport type { PathGuard } from './core/path.ts';\nimport type { ResourceStore } from './core/store.ts';\nimport { entryReadResult } from './core/store.ts';\nimport { getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES } from './core/util.ts';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from './core/watcher-registry.ts';\nimport {\n buildSectionsRecord,\n INSTRUCTIONS_SUMMARY,\n INSTRUCTIONS_URI,\n renderSections,\n} from './instructions.ts';\n\n// ═══════════════════════════════════════════════════════════════\n// shared\n// ═══════════════════════════════════════════════════════════════\n\nexport interface ResourceRegistrationOptions {\n resourceStore: ResourceStore;\n pathGuard: PathGuard;\n /** Mirrors the `--read-only` gate so the instructions match the tools actually registered. */\n readOnly: boolean;\n /**\n * Shared file-watcher registry for the modern (per-request) HTTP leg. When set,\n * the resource uses this handler-scoped registry instead of creating its own\n * per-server one, so file-change watchers persist across requests and publish\n * to the shared ServerEventBus. Omitted (per-server registry) on legacy/stdio.\n */\n watcherRegistry?: WatcherRegistry;\n /** Modern-leg typed notification publisher. */\n notifier?: ServerNotifier;\n /** The protocol era this instance serves; omitted where the caller does not know. */\n era?: 'legacy' | 'modern';\n /** Only stdio has the SDK's fixed-size read buffer. */\n transport?: 'stdio' | 'http';\n /**\n * Scope for shared-content cache hints. 'private' when the endpoint is\n * API-key gated, mirroring the list hints in server.ts — a shared cache must\n * not store an authenticated response body. Defaults to 'public'.\n */\n cacheScope?: 'public' | 'private';\n}\n\ntype ResourceRegistrarDeps = Omit<ResourceRegistrationOptions, 'pathGuard'> & {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n};\n\n/**\n * A filesystem failure that reads as not-found on the resource wire, for both\n * `resources/read` and `resources/subscribe`. ACCESS_DENIED folds in\n * deliberately: masking whether an out-of-root or denylisted path exists is the\n * security-correct answer, and the `FsError` message still carries the real\n * cause to the client.\n */\nfunction isNotFoundish(error: unknown): error is FsError {\n return (\n isFsError(error) &&\n (error.code === ErrorCode.NOT_FOUND || error.code === ErrorCode.ACCESS_DENIED)\n );\n}\n\n// ═══════════════════════════════════════════════════════════════\n// contract\n// ═══════════════════════════════════════════════════════════════\n\ninterface ResourceContract {\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n cacheHint?: CacheHint;\n annotations?: {\n audience?: Role[];\n priority?: number;\n };\n /** A fixed, enumerable URI (e.g. internal://instructions)... */\n uri?: string;\n /** ...or a URI template, optionally with a completer for its variable. */\n uriTemplate?: string;\n complete?: { path: CompleteResourceTemplateCallback };\n readonly read: (\n uri: URL,\n variables: Record<string, string | string[]>,\n ctx: ServerContext,\n ) => Promise<ReadResourceResult> | ReadResourceResult;\n readonly subscribe?: (\n uri: string,\n notify: (uri: string) => void,\n ) => Promise<boolean | undefined> | boolean | undefined;\n readonly unsubscribe?: (uri: string) => void;\n readonly destroy?: () => void;\n readonly list?: (\n ctx: ServerContext,\n ) => Promise<{ resources: Resource[] }> | { resources: Resource[] };\n}\n\n/** The one contract with a live watcher behind it. */\ntype SubscribableContract = ResourceContract &\n Required<Pick<ResourceContract, 'subscribe' | 'unsubscribe'>>;\n\n// ═══════════════════════════════════════════════════════════════\n// instructions\n// ═══════════════════════════════════════════════════════════════\n\nfunction createInstructionsResource(options: ResourceRegistrationOptions): ResourceContract {\n const text = renderSections(buildSectionsRecord(options.readOnly));\n return {\n name: 'filesystem-mcp-instructions',\n title: 'Server Instructions',\n description: INSTRUCTIONS_SUMMARY,\n mimeType: 'text/markdown',\n uri: INSTRUCTIONS_URI,\n annotations: { audience: ['assistant'], priority: 0.8 },\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 300_000 },\n read(uri) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text,\n },\n ],\n };\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// filesystem\n// ═══════════════════════════════════════════════════════════════\n\nfunction createFilesystemResource(options: ResourceRegistrationOptions): SubscribableContract {\n const registry = options.watcherRegistry ?? createWatcherRegistry();\n // Only the per-server (legacy/stdio) registry is owned by this resource and\n // destroyed on dispose; the shared modern-leg registry is owned by the host\n // and torn down at server shutdown.\n const ownsRegistry = options.watcherRegistry === undefined;\n // The URIs this connection holds a `resources/subscribe` lease for. The wire\n // verb is per-URI and idempotent — a second subscribe is the same\n // subscription, and one unsubscribe ends it — but the registry ref-counts\n // leases (the HTTP listen leg needs that). Without this set a double\n // subscribe took two leases that one unsubscribe could not release, and an\n // unsubscribe for a URI never subscribed released a lease this connection\n // never took, dropping a watcher some other holder still wanted.\n const leasedUris = new Set<string>();\n // Subscribes mid-acquire, keyed by URI. Two concurrent subscribes for the\n // same URI must share one acquire: both passing the `leasedUris` check\n // before either finished took two registry leases that the single set entry\n // could release only once — on the shared modern registry that orphan lease\n // pinned a watcher until process shutdown.\n const inflight = new Map<string, Promise<boolean | undefined>>();\n\n return {\n name: 'filesystem-mcp-file',\n title: 'Workspace File',\n description: 'Read a file from the workspace. Subscribe to get updates when the file changes.',\n uriTemplate: FILESYSTEM_FILE_URI_TEMPLATE,\n annotations: { audience: ['assistant'], priority: 0.8 },\n // Without this the SDK falls back to its conservative `{ ttlMs: 0,\n // cacheScope: 'private' }`, making every file read uncacheable — and this\n // is the one resource with a live invalidation channel, so TTL and the\n // watcher compose exactly as the spec's caching section describes. The TTL\n // stays short because a non-subscriber gets no invalidation signal at all.\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 5_000 },\n\n // No `list`: the template registers with `list: undefined` — readable but\n // not enumerable. Listing each allowed root as a concrete resource\n // duplicated the template and grew resources/list linearly with roots;\n // `list_roots` owns root discovery.\n\n async read(uri, _variables, ctx: ServerContext) {\n ctx.mcpReq.signal.throwIfAborted();\n // Decode via extractPath — the same decoder resources/subscribe uses — so\n // both consumers are symmetric with buildFileResourceUri's encoding. The\n // {+path} template variable arrives still percent-encoded, so validating\n // it directly would treat \"c%3A/proj/a.ts\" as a literal relative path;\n // no fallback to it here, or the two branches would validate different\n // strings. extractPath only fails past the template match on undecodable\n // percent-encoding, which is the caller's malformed URI.\n const rawPath = extractPath(uri.href);\n if (rawPath === undefined) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed file URI: path is not valid percent-encoding',\n );\n }\n await options.pathGuard.validateExistingPath(rawPath);\n const fs = new GuardedFileSystem(options.pathGuard);\n const stdio = options.transport === 'stdio';\n if (stdio) {\n // Text encodes to at least its own bytes and base64 to 4/3 of them,\n // so a file over the limit is refused before it is read. One past the\n // file size limit is left to readRaw, whose refusal names that limit:\n // HTTP would not serve it either.\n const { stats } = await fs.stat(rawPath, { signal: ctx.mcpReq.signal });\n if (stats.size > MAX_OUTBOUND_RESULT_BYTES && stats.size <= getMaxTextFileSize()) {\n throw tooLargeForStdio(stats.size, true);\n }\n }\n const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });\n if (stdio && readResult.isBinary) {\n const base64Bytes = 4 * Math.ceil(readResult.content.length / 3);\n if (base64Bytes > MAX_OUTBOUND_RESULT_BYTES) throw tooLargeForStdio(base64Bytes, true);\n }\n\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: readResult.mimeType || 'application/octet-stream',\n ...(readResult.isBinary\n ? { blob: readResult.content.toString('base64') }\n : { text: readResult.content.toString('utf-8') }),\n },\n ],\n };\n },\n\n complete: {\n // Both ends speak the `{+path}` form (see encodeFileUriPath), not raw OS\n // paths: the partial arriving here is whatever this returned last, so the\n // decode mirrors the encode. An undecodable partial is matched as typed.\n path: async (value) => {\n const suggestions = await suggestPaths(\n options.pathGuard,\n decodeFileUriPath(value) ?? value,\n );\n return suggestions.map(encodeFileUriPath);\n },\n },\n\n subscribe(uri, notify) {\n // Already subscribed on this connection with a live watcher: this is a\n // no-op success rather than a second lease. If the OS watcher errored and\n // was dropped, release the dead lease and re-acquire.\n if (leasedUris.has(uri)) {\n if (registry.hasWatcher(uri)) return undefined;\n leasedUris.delete(uri);\n registry.release(uri);\n }\n // A subscribe for this URI is mid-acquire: share its outcome instead of\n // taking a second lease the lease set cannot represent.\n const pending = inflight.get(uri);\n if (pending) return pending;\n\n const acquire = async (): Promise<boolean | undefined> => {\n const result = await registry.acquire(options.pathGuard, uri, notify, {\n markSubscribe: true,\n });\n if (result.ok) {\n leasedUris.add(uri);\n return undefined;\n }\n\n if (result.reason === 'bad-uri') {\n throw new ResourceNotFoundError(uri, `Cannot subscribe: not a filesystem URI`);\n }\n if (result.reason === 'invalid-path') {\n const err = result.error;\n if (isNotFoundish(err)) {\n throw new ResourceNotFoundError(uri, `Cannot subscribe to ${uri}: ${err.message}`);\n }\n if (isFsError(err)) throw fsErrorToProtocolError(err);\n Logger.warn(\n `Unexpected error validating path for watcher ${uri}: ${formatUnknownErrorMessage(err)}`,\n );\n throw err;\n }\n // Unsubscribed (or the registry destroyed) mid-await: the caller already\n // asked for this to stop, so there is nothing to reject.\n if (result.reason === 'stale') return undefined;\n // capped / attach-failed: `false` tells the handler to reject, since\n // undefined would report success with no watcher attached.\n return false;\n };\n\n const promise = acquire().finally(() => inflight.delete(uri));\n inflight.set(uri, promise);\n return promise;\n },\n\n unsubscribe(uri) {\n // Only release a lease this connection actually took.\n if (!leasedUris.delete(uri)) return;\n registry.release(uri);\n },\n\n destroy() {\n // Release what this connection still holds before the registry goes: on\n // the shared (modern) registry nothing else would ever end these leases.\n for (const uri of leasedUris) registry.release(uri);\n leasedUris.clear();\n if (ownsRegistry) registry.destroy();\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// result\n// ═══════════════════════════════════════════════════════════════\n\nfunction createResultResource(options: ResourceRegistrationOptions): ResourceContract {\n return {\n name: 'filesystem-mcp-result',\n title: 'Cached Tool Result',\n description:\n 'Ephemeral cached tool output. Listed via resources/list; entries expire after the cache TTL.',\n mimeType: 'application/json',\n uriTemplate: 'filesystem-mcp://result/{id}',\n annotations: { audience: ['assistant'], priority: 0.3 },\n cacheHint: { cacheScope: 'private', ttlMs: 60_000 },\n list() {\n const store = options.resourceStore;\n const uris = store.keys(); // prunes expired first\n const resources: Resource[] = [];\n for (const uri of uris) {\n try {\n const entry = store.getEntry(uri);\n resources.push({\n uri: entry.uri,\n name: entry.name,\n description: `Cached tool result; expires ${entry.expiresAt}`,\n mimeType: entry.mimeType,\n size: entry.size,\n annotations: { audience: ['assistant'], priority: 0.3, lastModified: entry.createdAt },\n });\n } catch (err) {\n // An entry may expire between keys() and getEntry; skip it.\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) continue;\n throw err;\n }\n }\n // Recent-first: keys() returns insertion order, so the newest is last.\n resources.reverse();\n return { resources };\n },\n read(uri, variables) {\n const { id } = variables;\n if (typeof id !== 'string' || id.length === 0) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed result URI: missing {id}. Use the exact resourceUri returned by the tool.',\n );\n }\n\n let entry;\n try {\n entry = options.resourceStore.getEntry(uri.toString());\n } catch (err) {\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) {\n throw new ResourceNotFoundError(\n uri.toString(),\n 'Cached result not found or expired. Re-run the tool to regenerate.',\n );\n }\n throw err;\n }\n return entryReadResult(entry);\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// export contracts\n// ═══════════════════════════════════════════════════════════════\n\n// The file template last and typed as subscribable, so the registrar can\n// wire its subscribe handlers from the same list it registers.\nexport function getResourceContracts(\n options: ResourceRegistrationOptions,\n): readonly [ResourceContract, ResourceContract, SubscribableContract] {\n return [\n createInstructionsResource(options),\n createResultResource(options),\n createFilesystemResource(options),\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════\n// registrar\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * A caller-traceable FsError as the SDK's InvalidParams error. The Problem's\n * identity fields ride the SDK's error.data channel: `message` stays the\n * wire message, and code/path/suggestion give clients a machine-readable\n * view. Keyed by `code`, so it cannot collide with the\n * ResourceNotFoundError convention (data: { uri } and nothing else).\n */\nfunction fsErrorToProtocolError(error: FsError): ProtocolError {\n const { code, path, suggestion } = error.problem;\n return new ProtocolError(ProtocolErrorCode.InvalidParams, error.message, {\n code,\n ...(path !== undefined ? { path } : {}),\n ...(suggestion !== undefined ? { suggestion } : {}),\n });\n}\n\n/**\n * The refusal for a result stdio's fixed-size reader cannot carry. `atLeast`\n * when `bytes` is a lower bound known before the result was encoded.\n */\nfunction tooLargeForStdio(bytes: number, atLeast = false): ProtocolError {\n const suggestion = 'Read smaller slices/pages with tools, or connect over HTTP.';\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource result too large to return (${atLeast ? 'at least ' : ''}${String(bytes)} bytes once JSON-encoded, over the ${String(MAX_OUTBOUND_RESULT_BYTES)}-byte stdio message limit). ${suggestion}`,\n { code: ErrorCode.TOO_LARGE, suggestion },\n );\n}\n\nfunction wrapRead(contract: ResourceContract, transport: ResourceRegistrationOptions['transport']) {\n return async (uri: URL, variables: Record<string, string | string[]>, ctx: ServerContext) => {\n try {\n const result = await contract.read(uri, variables, ctx);\n if (transport === 'stdio') {\n // Cache/file bytes exclude outer JSON escaping and binary base64.\n const encodedBytes = jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES);\n if (encodedBytes !== undefined) throw tooLargeForStdio(encodedBytes);\n }\n return result;\n } catch (error) {\n // The SDK brands ProtocolError with `static [Symbol.hasInstance]`, so this\n // holds across separately bundled SDK copies and realms.\n if (error instanceof ProtocolError) throw error;\n // A missing or out-of-root path is a not-found, not a malformed request.\n // The SDK puts ResourceNotFoundError on the wire as -32602 with\n // `data.uri`, which is what clients match on; -32002 is the older code\n // they also accept, and this SDK never emits it.\n if (isNotFoundish(error)) {\n throw new ResourceNotFoundError(uri.toString(), error.message);\n }\n // A remaining FsError (NOT_FILE, TOO_LARGE, ...) traces to the\n // caller-supplied URI; anything else is a server-side failure and must\n // not be blamed on the request.\n if (isFsError(error)) throw fsErrorToProtocolError(error);\n throw new ProtocolError(ProtocolErrorCode.InternalError, formatUnknownErrorMessage(error));\n }\n };\n}\n\n/**\n * Whether `contract` answers for `uri`, decided by the template matcher the SDK\n * dispatches `resources/read` with, so `resources/subscribe` and\n * `resources/read` agree on which resource owns a URI. Not\n * `checkResourceAllowed`: that is an RFC 8707 audience check comparing\n * `URL.origin`, which is the opaque `\"null\"` for every custom scheme, so it\n * matched `filesystem-mcp://result/…` — and any other scheme — to the file\n * template.\n */\nconst TEMPLATE_MATCHERS = new Map<string, UriTemplate>();\n\nfunction owns(contract: ResourceContract, uri: string): boolean {\n if (contract.uri !== undefined) return uri === contract.uri;\n if (contract.uriTemplate === undefined) return false;\n let matcher = TEMPLATE_MATCHERS.get(contract.uriTemplate);\n if (!matcher) {\n matcher = new UriTemplate(contract.uriTemplate);\n TEMPLATE_MATCHERS.set(contract.uriTemplate, matcher);\n }\n return matcher.match(uri) !== null;\n}\n\nexport function registerResources(deps: ResourceRegistrarDeps): { dispose(): void } {\n const server = deps.server;\n const resourceContracts = getResourceContracts(deps);\n const file = resourceContracts[2];\n\n for (const contract of resourceContracts) {\n const config = {\n ...(contract.title !== undefined ? { title: contract.title } : {}),\n ...(contract.description !== undefined ? { description: contract.description } : {}),\n ...(contract.mimeType !== undefined ? { mimeType: contract.mimeType } : {}),\n ...(contract.annotations !== undefined ? { annotations: contract.annotations } : {}),\n ...(contract.cacheHint !== undefined ? { cacheHint: contract.cacheHint } : {}),\n };\n\n if (contract.uriTemplate) {\n const template = new ResourceTemplate(contract.uriTemplate, {\n list: contract.list,\n ...(contract.complete ? { complete: contract.complete } : {}),\n });\n\n server.registerResource(contract.name, template, config, wrapRead(contract, deps.transport));\n } else if (contract.uri) {\n server.registerResource(contract.name, contract.uri, config, (uri: URL, ctx: ServerContext) =>\n wrapRead(contract, deps.transport)(uri, {}, ctx),\n );\n }\n }\n\n // `resources/subscribe`/`unsubscribe` are 2025-era-only verbs; a modern\n // server answers `-32601 Method not found` for them, so registering these\n // handlers on a modern-era instance would dispatch to code no request can\n // reach. A legacy instance on the HTTP leg (the one built with a `notifier`)\n // lives for one request: a subscription accepted there would take a watcher\n // lease for an instance that is gone before the file changes. Leave the\n // handlers off so the SDK refuses the verb, and no lease is taken.\n // sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.\n const legacyHttp = deps.era === 'legacy' && deps.notifier !== undefined;\n if (deps.era !== 'modern' && !legacyHttp) {\n server.server.assertCanSetRequestHandler('resources/subscribe');\n server.server.assertCanSetRequestHandler('resources/unsubscribe');\n\n // One stable callback for the whole registrar, NOT one per subscribe. The\n // registry holds these in a Set keyed by identity, so a fresh closure per\n // request made a second `resources/subscribe` for the same URI register a\n // second sink: one file change then sent N notifications, and `unsubscribe`\n // (which only ends a lease) removed none of them. Subscribe is per-URI on\n // the wire, so the sink must be too — this makes it idempotent.\n const notifyUpdated = (updatedUri: string): void => {\n voidSend(server.server.sendResourceUpdated({ uri: updatedUri }), 'resource update', {\n uri: updatedUri,\n });\n };\n\n server.server.setRequestHandler(\n 'resources/subscribe',\n async (req: { params: SubscribeRequestParams }) => {\n const { uri } = req.params;\n if (!owns(file, uri)) {\n // A resource that exists but has no watcher (the instructions text,\n // a cached result) is NOT a not-found: reporting it as one told\n // clients a URI they can list and read does not exist.\n if (resourceContracts.some((contract) => owns(contract, uri))) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource ${uri} does not support subscriptions; only ${FILESYSTEM_FILE_URI_TEMPLATE} does. Read it again for the current contents.`,\n );\n }\n throw new ResourceNotFoundError(uri, `Resource not found: ${uri}`);\n }\n if ((await file.subscribe(uri, notifyUpdated)) === false) {\n // InternalError for want of anything better: ProtocolErrorCode has\n // no resource-limit member, and the message already names the\n // actionable cause.\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n `Subscription rejected: no watcher attached (watcher limit ${MAX_WATCHERS} reached, or fs.watch failed to start).`,\n );\n }\n return {};\n },\n );\n\n server.server.setRequestHandler(\n 'resources/unsubscribe',\n (req: { params: UnsubscribeRequestParams }) => {\n if (owns(file, req.params.uri)) file.unsubscribe(req.params.uri);\n return {};\n },\n );\n }\n\n return {\n dispose(): void {\n for (const contract of resourceContracts) {\n if (contract.destroy) {\n contract.destroy();\n }\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CacheHint,\n CompleteResourceTemplateCallback,\n McpServer,\n ReadResourceResult,\n Resource,\n Role,\n ServerContext,\n ServerNotifier,\n SubscribeRequestParams,\n UnsubscribeRequestParams,\n} from '@modelcontextprotocol/server';\nimport {\n ProtocolError,\n ProtocolErrorCode,\n ResourceNotFoundError,\n ResourceTemplate,\n UriTemplate,\n} from '@modelcontextprotocol/server';\n\nimport type { FsError } from './core/errors.ts';\nimport { ErrorCode, formatUnknownErrorMessage, isFsError } from './core/errors.ts';\nimport {\n decodeFileUriPath,\n encodeFileUriPath,\n extractPath,\n FILESYSTEM_FILE_URI_TEMPLATE,\n} from './core/file-uri.ts';\nimport { GuardedFileSystem } from './core/fs.ts';\nimport { Logger, voidSend } from './core/observability.ts';\nimport { suggestPaths } from './core/path-completer.ts';\nimport type { PathGuard } from './core/path.ts';\nimport type { ResourceStore } from './core/store.ts';\nimport { entryReadResult } from './core/store.ts';\nimport {\n base64EncodedBytes,\n getMaxTextFileSize,\n jsonBytesOver,\n MAX_OUTBOUND_RESULT_BYTES,\n} from './core/util.ts';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from './core/watcher-registry.ts';\nimport {\n buildSectionsRecord,\n INSTRUCTIONS_SUMMARY,\n INSTRUCTIONS_URI,\n renderSections,\n} from './instructions.ts';\n\n// ═══════════════════════════════════════════════════════════════\n// shared\n// ═══════════════════════════════════════════════════════════════\n\nexport interface ResourceRegistrationOptions {\n resourceStore: ResourceStore;\n pathGuard: PathGuard;\n /** Mirrors the `--read-only` gate so the instructions match the tools actually registered. */\n readOnly: boolean;\n /**\n * Shared file-watcher registry for the modern (per-request) HTTP leg. When set,\n * the resource uses this handler-scoped registry instead of creating its own\n * per-server one, so file-change watchers persist across requests and publish\n * to the shared ServerEventBus. Omitted (per-server registry) on legacy/stdio.\n */\n watcherRegistry?: WatcherRegistry;\n /** Modern-leg typed notification publisher. */\n notifier?: ServerNotifier;\n /** The protocol era this instance serves; omitted where the caller does not know. */\n era?: 'legacy' | 'modern';\n /** Only stdio has the SDK's fixed-size read buffer. */\n transport?: 'stdio' | 'http';\n /**\n * Scope for shared-content cache hints. 'private' when the endpoint is\n * API-key gated, mirroring the list hints in server.ts — a shared cache must\n * not store an authenticated response body. Defaults to 'public'.\n */\n cacheScope?: 'public' | 'private';\n}\n\ntype ResourceRegistrarDeps = Omit<ResourceRegistrationOptions, 'pathGuard'> & {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n};\n\n/**\n * A filesystem failure that reads as not-found on the resource wire, for both\n * `resources/read` and `resources/subscribe`. ACCESS_DENIED folds in\n * deliberately: masking whether an out-of-root or denylisted path exists is the\n * security-correct answer, and the `FsError` message still carries the real\n * cause to the client.\n */\nfunction isNotFoundish(error: unknown): error is FsError {\n return (\n isFsError(error) &&\n (error.code === ErrorCode.NOT_FOUND || error.code === ErrorCode.ACCESS_DENIED)\n );\n}\n\n// ═══════════════════════════════════════════════════════════════\n// contract\n// ═══════════════════════════════════════════════════════════════\n\ninterface ResourceContract {\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n cacheHint?: CacheHint;\n annotations?: {\n audience?: Role[];\n priority?: number;\n };\n /** A fixed, enumerable URI (e.g. internal://instructions)... */\n uri?: string;\n /** ...or a URI template, optionally with a completer for its variable. */\n uriTemplate?: string;\n complete?: { path: CompleteResourceTemplateCallback };\n readonly read: (\n uri: URL,\n variables: Record<string, string | string[]>,\n ctx: ServerContext,\n ) => Promise<ReadResourceResult> | ReadResourceResult;\n readonly subscribe?: (\n uri: string,\n notify: (uri: string) => void,\n ) => Promise<boolean | undefined> | boolean | undefined;\n readonly unsubscribe?: (uri: string) => void;\n readonly destroy?: () => void;\n readonly list?: (\n ctx: ServerContext,\n ) => Promise<{ resources: Resource[] }> | { resources: Resource[] };\n}\n\n/** The one contract with a live watcher behind it. */\ntype SubscribableContract = ResourceContract &\n Required<Pick<ResourceContract, 'subscribe' | 'unsubscribe'>>;\n\n// ═══════════════════════════════════════════════════════════════\n// instructions\n// ═══════════════════════════════════════════════════════════════\n\nfunction createInstructionsResource(options: ResourceRegistrationOptions): ResourceContract {\n const text = renderSections(buildSectionsRecord(options.readOnly));\n return {\n name: 'filesystem-mcp-instructions',\n title: 'Server Instructions',\n description: INSTRUCTIONS_SUMMARY,\n mimeType: 'text/markdown',\n uri: INSTRUCTIONS_URI,\n annotations: { audience: ['assistant'], priority: 0.8 },\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 300_000 },\n read(uri) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text,\n },\n ],\n };\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// filesystem\n// ═══════════════════════════════════════════════════════════════\n\nfunction createFilesystemResource(options: ResourceRegistrationOptions): SubscribableContract {\n const registry = options.watcherRegistry ?? createWatcherRegistry();\n // Only the per-server (legacy/stdio) registry is owned by this resource and\n // destroyed on dispose; the shared modern-leg registry is owned by the host\n // and torn down at server shutdown.\n const ownsRegistry = options.watcherRegistry === undefined;\n // The URIs this connection holds a `resources/subscribe` lease for. The wire\n // verb is per-URI and idempotent — a second subscribe is the same\n // subscription, and one unsubscribe ends it — but the registry ref-counts\n // leases (the HTTP listen leg needs that). Without this set a double\n // subscribe took two leases that one unsubscribe could not release, and an\n // unsubscribe for a URI never subscribed released a lease this connection\n // never took, dropping a watcher some other holder still wanted.\n const leasedUris = new Set<string>();\n // Subscribes mid-acquire, keyed by URI. Two concurrent subscribes for the\n // same URI must share one acquire: both passing the `leasedUris` check\n // before either finished took two registry leases that the single set entry\n // could release only once — on the shared modern registry that orphan lease\n // pinned a watcher until process shutdown.\n const inflight = new Map<string, Promise<boolean | undefined>>();\n\n return {\n name: 'filesystem-mcp-file',\n title: 'Workspace File',\n description: 'Read a file from the workspace. Subscribe to get updates when the file changes.',\n uriTemplate: FILESYSTEM_FILE_URI_TEMPLATE,\n annotations: { audience: ['assistant'], priority: 0.8 },\n // Without this the SDK falls back to its conservative `{ ttlMs: 0,\n // cacheScope: 'private' }`, making every file read uncacheable — and this\n // is the one resource with a live invalidation channel, so TTL and the\n // watcher compose exactly as the spec's caching section describes. The TTL\n // stays short because a non-subscriber gets no invalidation signal at all.\n cacheHint: { cacheScope: options.cacheScope ?? 'public', ttlMs: 5_000 },\n\n // No `list`: the template registers with `list: undefined` — readable but\n // not enumerable. Listing each allowed root as a concrete resource\n // duplicated the template and grew resources/list linearly with roots;\n // `list_roots` owns root discovery.\n\n async read(uri, _variables, ctx: ServerContext) {\n ctx.mcpReq.signal.throwIfAborted();\n // Decode via extractPath — the same decoder resources/subscribe uses — so\n // both consumers are symmetric with buildFileResourceUri's encoding. The\n // {+path} template variable arrives still percent-encoded, so validating\n // it directly would treat \"c%3A/proj/a.ts\" as a literal relative path;\n // no fallback to it here, or the two branches would validate different\n // strings. extractPath only fails past the template match on undecodable\n // percent-encoding, which is the caller's malformed URI.\n const rawPath = extractPath(uri.href);\n if (rawPath === undefined) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed file URI: path is not valid percent-encoding',\n );\n }\n await options.pathGuard.validateExistingPath(rawPath);\n const fs = new GuardedFileSystem(options.pathGuard);\n const stdio = options.transport === 'stdio';\n if (stdio) {\n // Text encodes to at least its own bytes and base64 to 4/3 of them,\n // so a file over the limit is refused before it is read. One past the\n // file size limit is left to readRaw, whose refusal names that limit:\n // HTTP would not serve it either.\n const { stats } = await fs.stat(rawPath, { signal: ctx.mcpReq.signal });\n if (stats.size > MAX_OUTBOUND_RESULT_BYTES && stats.size <= getMaxTextFileSize()) {\n throw tooLargeForStdio(stats.size, true);\n }\n }\n const readResult = await fs.readRaw(rawPath, { signal: ctx.mcpReq.signal });\n if (stdio && readResult.isBinary) {\n const base64Bytes = base64EncodedBytes(readResult.content.length);\n if (base64Bytes > MAX_OUTBOUND_RESULT_BYTES) throw tooLargeForStdio(base64Bytes, true);\n }\n\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: readResult.mimeType || 'application/octet-stream',\n ...(readResult.isBinary\n ? { blob: readResult.content.toString('base64') }\n : { text: readResult.content.toString('utf-8') }),\n },\n ],\n };\n },\n\n complete: {\n // Both ends speak the `{+path}` form (see encodeFileUriPath), not raw OS\n // paths: the partial arriving here is whatever this returned last, so the\n // decode mirrors the encode. An undecodable partial is matched as typed.\n path: async (value) => {\n const suggestions = await suggestPaths(\n options.pathGuard,\n decodeFileUriPath(value) ?? value,\n );\n return suggestions.map(encodeFileUriPath);\n },\n },\n\n subscribe(uri, notify) {\n // Already subscribed on this connection with a live watcher: this is a\n // no-op success rather than a second lease. If the OS watcher errored and\n // was dropped, release the dead lease and re-acquire.\n if (leasedUris.has(uri)) {\n if (registry.hasWatcher(uri)) return undefined;\n leasedUris.delete(uri);\n registry.release(uri);\n }\n // A subscribe for this URI is mid-acquire: share its outcome instead of\n // taking a second lease the lease set cannot represent.\n const pending = inflight.get(uri);\n if (pending) return pending;\n\n const acquire = async (): Promise<boolean | undefined> => {\n const result = await registry.acquire(options.pathGuard, uri, notify, {\n markSubscribe: true,\n });\n if (result.ok) {\n leasedUris.add(uri);\n return undefined;\n }\n\n if (result.reason === 'bad-uri') {\n throw new ResourceNotFoundError(uri, `Cannot subscribe: not a filesystem URI`);\n }\n if (result.reason === 'invalid-path') {\n const err = result.error;\n if (isNotFoundish(err)) {\n throw new ResourceNotFoundError(uri, `Cannot subscribe to ${uri}: ${err.message}`);\n }\n if (isFsError(err)) throw fsErrorToProtocolError(err);\n Logger.warn(\n `Unexpected error validating path for watcher ${uri}: ${formatUnknownErrorMessage(err)}`,\n );\n throw err;\n }\n // Unsubscribed (or the registry destroyed) mid-await: the caller already\n // asked for this to stop, so there is nothing to reject.\n if (result.reason === 'stale') return undefined;\n // capped / attach-failed: `false` tells the handler to reject, since\n // undefined would report success with no watcher attached.\n return false;\n };\n\n const promise = acquire().finally(() => inflight.delete(uri));\n inflight.set(uri, promise);\n return promise;\n },\n\n unsubscribe(uri) {\n // Only release a lease this connection actually took.\n if (!leasedUris.delete(uri)) return;\n registry.release(uri);\n },\n\n destroy() {\n // Release what this connection still holds before the registry goes: on\n // the shared (modern) registry nothing else would ever end these leases.\n for (const uri of leasedUris) registry.release(uri);\n leasedUris.clear();\n if (ownsRegistry) registry.destroy();\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// result\n// ═══════════════════════════════════════════════════════════════\n\nfunction createResultResource(options: ResourceRegistrationOptions): ResourceContract {\n return {\n name: 'filesystem-mcp-result',\n title: 'Cached Tool Result',\n description:\n 'Ephemeral cached tool output. Listed via resources/list; entries expire after the cache TTL.',\n mimeType: 'application/json',\n uriTemplate: 'filesystem-mcp://result/{id}',\n annotations: { audience: ['assistant'], priority: 0.3 },\n cacheHint: { cacheScope: 'private', ttlMs: 60_000 },\n list() {\n const store = options.resourceStore;\n const uris = store.keys(); // prunes expired first\n const resources: Resource[] = [];\n for (const uri of uris) {\n try {\n const entry = store.getEntry(uri);\n resources.push({\n uri: entry.uri,\n name: entry.name,\n description: `Cached tool result; expires ${entry.expiresAt}`,\n mimeType: entry.mimeType,\n size: entry.size,\n annotations: { audience: ['assistant'], priority: 0.3, lastModified: entry.createdAt },\n });\n } catch (err) {\n // An entry may expire between keys() and getEntry; skip it.\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) continue;\n throw err;\n }\n }\n // Recent-first: keys() returns insertion order, so the newest is last.\n resources.reverse();\n return { resources };\n },\n read(uri, variables) {\n const { id } = variables;\n if (typeof id !== 'string' || id.length === 0) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n 'Malformed result URI: missing {id}. Use the exact resourceUri returned by the tool.',\n );\n }\n\n let entry;\n try {\n entry = options.resourceStore.getEntry(uri.toString());\n } catch (err) {\n if (isFsError(err) && err.code === ErrorCode.NOT_FOUND) {\n throw new ResourceNotFoundError(\n uri.toString(),\n 'Cached result not found or expired. Re-run the tool to regenerate.',\n );\n }\n throw err;\n }\n return entryReadResult(entry);\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// export contracts\n// ═══════════════════════════════════════════════════════════════\n\n// The file template last and typed as subscribable, so the registrar can\n// wire its subscribe handlers from the same list it registers.\nexport function getResourceContracts(\n options: ResourceRegistrationOptions,\n): readonly [ResourceContract, ResourceContract, SubscribableContract] {\n return [\n createInstructionsResource(options),\n createResultResource(options),\n createFilesystemResource(options),\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════\n// registrar\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * A caller-traceable FsError as the SDK's InvalidParams error. The Problem's\n * identity fields ride the SDK's error.data channel: `message` stays the\n * wire message, and code/path/suggestion give clients a machine-readable\n * view. Keyed by `code`, so it cannot collide with the\n * ResourceNotFoundError convention (data: { uri } and nothing else).\n */\nfunction fsErrorToProtocolError(error: FsError): ProtocolError {\n const { code, path, suggestion } = error.problem;\n return new ProtocolError(ProtocolErrorCode.InvalidParams, error.message, {\n code,\n ...(path !== undefined ? { path } : {}),\n ...(suggestion !== undefined ? { suggestion } : {}),\n });\n}\n\n/**\n * The refusal for a result stdio's fixed-size reader cannot carry. `atLeast`\n * when `bytes` is a lower bound known before the result was encoded.\n */\nfunction tooLargeForStdio(bytes: number, atLeast = false): ProtocolError {\n const suggestion = 'Read smaller slices/pages with tools, or connect over HTTP.';\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource result too large to return (${atLeast ? 'at least ' : ''}${String(bytes)} bytes once JSON-encoded, over the ${String(MAX_OUTBOUND_RESULT_BYTES)}-byte stdio message limit). ${suggestion}`,\n { code: ErrorCode.TOO_LARGE, suggestion },\n );\n}\n\nfunction wrapRead(contract: ResourceContract, transport: ResourceRegistrationOptions['transport']) {\n return async (uri: URL, variables: Record<string, string | string[]>, ctx: ServerContext) => {\n try {\n const result = await contract.read(uri, variables, ctx);\n if (transport === 'stdio') {\n // Cache/file bytes exclude outer JSON escaping and binary base64.\n const encodedBytes = jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES);\n if (encodedBytes !== undefined) throw tooLargeForStdio(encodedBytes);\n }\n return result;\n } catch (error) {\n // The SDK brands ProtocolError with `static [Symbol.hasInstance]`, so this\n // holds across separately bundled SDK copies and realms.\n if (error instanceof ProtocolError) throw error;\n // A missing or out-of-root path is a not-found, not a malformed request.\n // The SDK puts ResourceNotFoundError on the wire as -32602 with\n // `data.uri`, which is what clients match on; -32002 is the older code\n // they also accept, and this SDK never emits it.\n if (isNotFoundish(error)) {\n throw new ResourceNotFoundError(uri.toString(), error.message);\n }\n // A remaining FsError (NOT_FILE, TOO_LARGE, ...) traces to the\n // caller-supplied URI; anything else is a server-side failure and must\n // not be blamed on the request.\n if (isFsError(error)) throw fsErrorToProtocolError(error);\n throw new ProtocolError(ProtocolErrorCode.InternalError, formatUnknownErrorMessage(error));\n }\n };\n}\n\n/**\n * Whether `contract` answers for `uri`, decided by the template matcher the SDK\n * dispatches `resources/read` with, so `resources/subscribe` and\n * `resources/read` agree on which resource owns a URI. Not\n * `checkResourceAllowed`: that is an RFC 8707 audience check comparing\n * `URL.origin`, which is the opaque `\"null\"` for every custom scheme, so it\n * matched `filesystem-mcp://result/…` — and any other scheme — to the file\n * template.\n */\nconst TEMPLATE_MATCHERS = new Map<string, UriTemplate>();\n\nfunction owns(contract: ResourceContract, uri: string): boolean {\n if (contract.uri !== undefined) return uri === contract.uri;\n if (contract.uriTemplate === undefined) return false;\n let matcher = TEMPLATE_MATCHERS.get(contract.uriTemplate);\n if (!matcher) {\n matcher = new UriTemplate(contract.uriTemplate);\n TEMPLATE_MATCHERS.set(contract.uriTemplate, matcher);\n }\n return matcher.match(uri) !== null;\n}\n\nexport function registerResources(deps: ResourceRegistrarDeps): { dispose(): void } {\n const server = deps.server;\n const resourceContracts = getResourceContracts(deps);\n const file = resourceContracts[2];\n\n for (const contract of resourceContracts) {\n const config = {\n ...(contract.title !== undefined ? { title: contract.title } : {}),\n ...(contract.description !== undefined ? { description: contract.description } : {}),\n ...(contract.mimeType !== undefined ? { mimeType: contract.mimeType } : {}),\n ...(contract.annotations !== undefined ? { annotations: contract.annotations } : {}),\n ...(contract.cacheHint !== undefined ? { cacheHint: contract.cacheHint } : {}),\n };\n\n if (contract.uriTemplate) {\n const template = new ResourceTemplate(contract.uriTemplate, {\n list: contract.list,\n ...(contract.complete ? { complete: contract.complete } : {}),\n });\n\n server.registerResource(contract.name, template, config, wrapRead(contract, deps.transport));\n } else if (contract.uri) {\n server.registerResource(contract.name, contract.uri, config, (uri: URL, ctx: ServerContext) =>\n wrapRead(contract, deps.transport)(uri, {}, ctx),\n );\n }\n }\n\n // `resources/subscribe`/`unsubscribe` are 2025-era-only verbs; a modern\n // server answers `-32601 Method not found` for them, so registering these\n // handlers on a modern-era instance would dispatch to code no request can\n // reach. A legacy instance on the HTTP leg (the one built with a `notifier`)\n // lives for one request: a subscription accepted there would take a watcher\n // lease for an instance that is gone before the file changes. Leave the\n // handlers off so the SDK refuses the verb, and no lease is taken.\n // sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.\n const legacyHttp = deps.era === 'legacy' && deps.notifier !== undefined;\n if (deps.era !== 'modern' && !legacyHttp) {\n server.server.assertCanSetRequestHandler('resources/subscribe');\n server.server.assertCanSetRequestHandler('resources/unsubscribe');\n\n // One stable callback for the whole registrar, NOT one per subscribe. The\n // registry holds these in a Set keyed by identity, so a fresh closure per\n // request made a second `resources/subscribe` for the same URI register a\n // second sink: one file change then sent N notifications, and `unsubscribe`\n // (which only ends a lease) removed none of them. Subscribe is per-URI on\n // the wire, so the sink must be too — this makes it idempotent.\n const notifyUpdated = (updatedUri: string): void => {\n voidSend(server.server.sendResourceUpdated({ uri: updatedUri }), 'resource update', {\n uri: updatedUri,\n });\n };\n\n server.server.setRequestHandler(\n 'resources/subscribe',\n async (req: { params: SubscribeRequestParams }) => {\n const { uri } = req.params;\n if (!owns(file, uri)) {\n // A resource that exists but has no watcher (the instructions text,\n // a cached result) is NOT a not-found: reporting it as one told\n // clients a URI they can list and read does not exist.\n if (resourceContracts.some((contract) => owns(contract, uri))) {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Resource ${uri} does not support subscriptions; only ${FILESYSTEM_FILE_URI_TEMPLATE} does. Read it again for the current contents.`,\n );\n }\n throw new ResourceNotFoundError(uri, `Resource not found: ${uri}`);\n }\n if ((await file.subscribe(uri, notifyUpdated)) === false) {\n // InternalError for want of anything better: ProtocolErrorCode has\n // no resource-limit member, and the message already names the\n // actionable cause.\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n `Subscription rejected: no watcher attached (watcher limit ${MAX_WATCHERS} reached, or fs.watch failed to start).`,\n );\n }\n return {};\n },\n );\n\n server.server.setRequestHandler(\n 'resources/unsubscribe',\n (req: { params: UnsubscribeRequestParams }) => {\n if (owns(file, req.params.uri)) file.unsubscribe(req.params.uri);\n return {};\n },\n );\n }\n\n return {\n dispose(): void {\n for (const contract of resourceContracts) {\n if (contract.destroy) {\n contract.destroy();\n }\n }\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAoTA,eAAO,MAAM,IAAI,mCA0Gf,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AA4TA,eAAO,MAAM,IAAI,mCA2Gf,CAAC"}