@j0hanz/filesystem-mcp 2.7.5 → 2.7.6
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.
- package/README.md +290 -394
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +15 -7
- package/dist/cli-help.js.map +1 -1
- package/dist/core/concurrency.d.ts +7 -0
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +19 -0
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/fs.d.ts +8 -0
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +62 -12
- package/dist/core/fs.js.map +1 -1
- package/dist/core/path.d.ts +7 -16
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +10 -26
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +4 -1
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +13 -3
- package/dist/core/read.js.map +1 -1
- package/dist/core/search.d.ts +7 -0
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +50 -15
- package/dist/core/search.js.map +1 -1
- package/dist/core/util.d.ts +8 -0
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +8 -0
- package/dist/core/util.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +98 -84
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +3 -0
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +1 -0
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +18 -5
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list.js +1 -1
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +35 -5
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +91 -86
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +16 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +38 -15
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +1 -0
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +1 -0
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/core/read.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"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","sourcesContent":["// File reading pipeline split out of fs.ts. fs.ts imports from here; never the reverse.\nimport { isUtf8 } from 'node:buffer';\nimport type { Stats } from 'node:fs';\nimport { open as fsOpen } from 'node:fs/promises';\nimport type { FileHandle } from 'node:fs/promises';\nimport { StringDecoder } from 'node:string_decoder';\n\nimport { withAbort } from './concurrency.ts';\nimport { ErrorCode, formatUnknownErrorMessage, FsError, isFsError } from './errors.ts';\nimport { hasUtf16Bom, isBinarySample, isKnownBinaryExtension, MIME_SAMPLE_SIZE } from './mime.ts';\nimport { Logger } from './observability.ts';\nimport { getMaxTextFileSize } from './util.ts';\n\nconst STREAM_CHUNK_SIZE = 64 * 1024;\n\nconst READ_ONLY_FILE_FLAG = 'r';\n\nasync function openReadableFileHandle(filePath: string, signal?: AbortSignal): Promise<FileHandle> {\n const handlePromise = fsOpen(filePath, READ_ONLY_FILE_FLAG);\n if (!signal) return handlePromise;\n try {\n return await withAbort(handlePromise, signal);\n } catch (error) {\n void handlePromise\n .then((handle) => {\n void handle.close().catch((closeErr: unknown) => {\n Logger.warn(\n `Failed to close file handle for ${filePath} after abort: ${formatUnknownErrorMessage(closeErr)}`,\n );\n });\n })\n .catch(() => {\n /* ignore open error */\n });\n throw error;\n }\n}\n\nasync function readProbe(handle: FileHandle, signal?: AbortSignal): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe(MIME_SAMPLE_SIZE);\n const { bytesRead } = await withAbort(handle.read(buffer, 0, MIME_SAMPLE_SIZE, 0), signal);\n\n if (bytesRead === 0) {\n return Buffer.alloc(0);\n }\n\n return buffer.subarray(0, bytesRead);\n}\n\nexport type ReadSpec =\n | { kind: 'full'; signal?: AbortSignal; strictUtf8?: boolean }\n | { kind: 'head'; lines: number; signal?: AbortSignal }\n | { kind: 'tail'; lines: number; signal?: AbortSignal }\n | { kind: 'range'; start: number; end?: number; signal?: AbortSignal };\n\ninterface ReadOptions {\n maxSize: number;\n signal?: AbortSignal;\n}\n\ninterface PartialReadResult {\n content: string;\n linesRead: number;\n hasMoreLines: boolean;\n}\n\nexport interface ReadFileResult {\n path: string;\n content: string;\n totalLines?: number;\n readMode: ReadSpec['kind'];\n modified: string;\n head?: number;\n tail?: number;\n startLine?: number;\n endLine?: number;\n linesRead?: number;\n hasMoreLines?: boolean;\n}\n\nexport function createTooLargeError(\n bytesRead: number,\n maxSize: number,\n requestedPath: string,\n): FsError {\n return new FsError(\n ErrorCode.TOO_LARGE,\n `File exceeds size limit (${bytesRead} > ${maxSize} bytes)`,\n requestedPath,\n );\n}\n\nexport async function readFileBufferWithLimit(\n handle: FileHandle,\n maxSize: number,\n requestedPath: string,\n signal?: AbortSignal,\n): Promise<Buffer> {\n const stream = handle.createReadStream({\n start: 0,\n highWaterMark: STREAM_CHUNK_SIZE,\n autoClose: false,\n emitClose: false,\n signal,\n });\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n try {\n for await (const chunk of stream) {\n const buffer = chunk as Buffer;\n totalSize += buffer.length;\n\n if (totalSize > maxSize) {\n stream.destroy();\n throw createTooLargeError(totalSize, maxSize, requestedPath);\n }\n\n chunks.push(buffer);\n }\n } finally {\n if (!stream.destroyed) {\n stream.on('error', (_err: unknown) => {\n /* suppress post-destroy error event */\n });\n stream.destroy();\n }\n }\n\n return Buffer.concat(chunks, totalSize);\n}\n\nexport const countLines = (content: string): number => {\n if (content.length === 0) return 0;\n const trimmed = content.endsWith('\\n') ? content.slice(0, -1) : content;\n return trimmed.split('\\n').length;\n};\n\nasync function peekHasMore(iterator: AsyncIterator<string>): Promise<boolean> {\n try {\n const { done } = await iterator.next();\n return !done;\n } catch (error) {\n // The peek only asks \"is there another line\", and the caller keeps nothing\n // it returns. An over-long line past the requested range still answers yes\n // — failing the whole read over a line outside it would reject e.g. lines\n // 1-2 of a file whose line 3 is one 50 KB blob.\n if (isFsError(error) && error.code === ErrorCode.TOO_LARGE) return true;\n throw error;\n }\n}\n\nconst LF = 0x0a;\n\n/**\n * Line iterator bounded by bytes rather than by the decoded string, so a file\n * that is one enormous line cannot be materialized before the size check runs.\n * `handle.readLines` decodes the whole line first and can OOM on such a file.\n *\n * Yields lines from `startLine` onward, 1-indexed. Lines before `startLine` are\n * counted and discarded without accumulating, so an over-long line the caller\n * skipped past does not fail the read. Line endings match `readLines`: split on\n * `\\n`, with a preceding `\\r` stripped, so CRLF input decodes identically.\n */\nasync function* readLinesBounded(\n handle: FileHandle,\n options: ReadOptions,\n filePath: string,\n startLine: number,\n): AsyncGenerator<string, void, undefined> {\n const stream = handle.createReadStream({\n start: 0,\n highWaterMark: STREAM_CHUNK_SIZE,\n autoClose: false,\n emitClose: false,\n ...(options.signal ? { signal: options.signal } : {}),\n });\n\n const decoder = new StringDecoder('utf-8');\n let pending = '';\n let pendingBytes = 0;\n let lineNumber = 0;\n\n const tooLong = (bytes: number): FsError =>\n new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (single line ${bytes} > ${options.maxSize} bytes). Use a narrower range or head.`,\n filePath,\n );\n\n try {\n for await (const chunk of stream) {\n const buffer = chunk as Buffer;\n let offset = 0;\n\n // Search loop: each iteration finds the next LF and advances offset past\n // it. Written as while-with-assignment so the re-search is visible, rather\n // than buried in a for \"increment\" slot that re-runs indexOf.\n let nl = buffer.indexOf(LF, offset);\n while (nl !== -1) {\n const segment = buffer.subarray(offset, nl);\n const lineBytes = pendingBytes + segment.length;\n lineNumber++;\n // Decode even when skipping: the decoder carries multi-byte\n // continuation state into the first line the caller keeps.\n const decoded = decoder.write(segment);\n if (lineNumber >= startLine) {\n if (lineBytes > options.maxSize) throw tooLong(lineBytes);\n yield (pending + decoded).replace(/\\r$/, '');\n }\n pending = '';\n pendingBytes = 0;\n offset = nl + 1;\n nl = buffer.indexOf(LF, offset);\n }\n\n const rest = buffer.subarray(offset);\n pendingBytes += rest.length;\n if (lineNumber + 1 < startLine) {\n decoder.write(rest); // still skipping; drop the text, keep decoder state\n } else {\n if (pendingBytes > options.maxSize) throw tooLong(pendingBytes);\n pending += decoder.write(rest);\n }\n }\n\n // Trailing line with no final newline. A file ending in `\\n` leaves\n // pendingBytes at 0 and emits nothing, matching readLines.\n if (pendingBytes > 0) {\n lineNumber++;\n if (lineNumber >= startLine) {\n if (pendingBytes > options.maxSize) throw tooLong(pendingBytes);\n yield pending + decoder.end();\n }\n }\n } finally {\n if (!stream.destroyed) {\n stream.on('error', (_err: unknown) => {\n /* suppress post-destroy error event */\n });\n stream.destroy();\n }\n }\n}\n\nasync function readRangeContent(\n handle: FileHandle,\n startLine: number,\n endLine: number | undefined,\n options: ReadOptions,\n filePath: string,\n): Promise<PartialReadResult> {\n options.signal?.throwIfAborted();\n\n const lines: string[] = [];\n let lineNumber = startLine - 1;\n let estimatedBytes = 0;\n const newlineBytes = Buffer.byteLength('\\n', 'utf-8');\n const stopAt = endLine ?? Number.POSITIVE_INFINITY;\n\n let hasMoreLines = false;\n\n // Byte-bounded: an over-long line throws TOO_LARGE while still a partial\n // buffer, before it can be decoded into one huge string.\n const iterator = readLinesBounded(handle, options, filePath, startLine)[Symbol.asyncIterator]();\n\n try {\n for (;;) {\n const { value: line, done } = await iterator.next();\n if (done) {\n break;\n }\n lineNumber++;\n\n if (lineNumber > stopAt) {\n hasMoreLines = true;\n break;\n }\n\n lines.push(line);\n\n estimatedBytes += Buffer.byteLength(line, 'utf-8') + newlineBytes;\n if (estimatedBytes > options.maxSize) {\n hasMoreLines = await peekHasMore(iterator);\n break;\n }\n\n if (lineNumber === stopAt) {\n hasMoreLines = await peekHasMore(iterator);\n break;\n }\n }\n } finally {\n await iterator.return();\n }\n\n return {\n content: lines.join('\\n'),\n linesRead: lines.length,\n hasMoreLines,\n };\n}\n\nasync function readTailContent(\n handle: FileHandle,\n tail: number,\n options: ReadOptions,\n filePath: string,\n): Promise<PartialReadResult> {\n options.signal?.throwIfAborted();\n\n const stats = await handle.stat();\n const fileSize = stats.size;\n if (fileSize === 0) {\n return {\n content: '',\n linesRead: 0,\n hasMoreLines: false,\n };\n }\n\n const encoding = 'utf-8';\n // Accumulate raw bytes from the end: decoding a chunk ending mid-codepoint\n // corrupts the trailing UTF-8 sequence into U+FFFD. 0x0A is single-byte and\n // never part of a multibyte sequence, so count newlines on the raw buffer and\n // decode the whole bounded buffer once at the end.\n let position = fileSize;\n const chunks: Buffer[] = [];\n let totalLen = 0;\n let newlines = 0;\n let stoppedByLimit = false;\n\n while (position > 0) {\n options.signal?.throwIfAborted();\n const chunkSize = Math.min(position, STREAM_CHUNK_SIZE);\n const buffer = Buffer.allocUnsafe(chunkSize);\n const { bytesRead } = await handle.read(buffer, 0, chunkSize, position - chunkSize);\n if (bytesRead < chunkSize) {\n // The file shrank under us. Bytes from `position` on are still a\n // contiguous suffix, so keep those and stop — splicing this short chunk in\n // would leave a hole (and, with an alloc'd buffer, NULs) mid-content.\n // `position` stays put, so the result is reported as having more lines.\n break;\n }\n position -= chunkSize;\n\n for (const byte of buffer) {\n if (byte === 0x0a) newlines++;\n }\n\n chunks.unshift(buffer);\n totalLen += chunkSize;\n\n // One newline more than `tail`: the oldest segment in the buffer starts\n // mid-line whenever we stop before the file start, and gets dropped below.\n if (newlines > tail) break;\n if (totalLen > options.maxSize) {\n stoppedByLimit = true;\n break;\n }\n }\n\n const allBytes = Buffer.concat(chunks, totalLen);\n let lines = allBytes.toString(encoding).split('\\n');\n\n // A trailing newline produces a spurious empty final segment; drop it.\n if (lines.length > 0 && lines[lines.length - 1] === '') {\n lines = lines.slice(0, -1);\n }\n // Normalize CRLF.\n lines = lines.map((l) => (l.endsWith('\\r') ? l.slice(0, -1) : l));\n\n // We stopped before the file start, so the first segment is the tail of a\n // line whose beginning was never read. Drop it instead of reporting a\n // fragment as a complete line.\n if (position > 0 && lines.length > 0) {\n lines = lines.slice(1);\n }\n\n if (stoppedByLimit && lines.length < tail) {\n throw new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (${totalLen} > ${options.maxSize} bytes, could not collect ${tail} lines). Use a narrower tail or head.`,\n filePath,\n );\n }\n\n let hasMoreLines = stoppedByLimit || position > 0;\n if (lines.length > tail) {\n hasMoreLines = true;\n lines = lines.slice(lines.length - tail);\n }\n\n const content = lines.join('\\n');\n return {\n content,\n linesRead: lines.length,\n hasMoreLines,\n };\n}\n\nasync function readFullContent(\n handle: FileHandle,\n maxSize: number,\n requestedPath: string,\n signal: AbortSignal | undefined,\n strictUtf8: boolean,\n): Promise<{ content: string; totalLines: number }> {\n const buffer = await readFileBufferWithLimit(handle, maxSize, requestedPath, signal);\n // The binary probe samples only the first 512 bytes. A caller that writes the\n // text back must refuse any byte the decode would replace with U+FFFD.\n if (strictUtf8 && (!isUtf8(buffer) || buffer.includes(0))) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary or non-UTF-8 file detected.', requestedPath);\n }\n const content = buffer.toString('utf-8');\n return { content, totalLines: countLines(content) };\n}\n\nasync function assertNotBinary(\n validPath: string,\n filePath: string,\n handle: FileHandle,\n normalized: ReadOptions,\n): Promise<void> {\n normalized.signal?.throwIfAborted();\n if (isKnownBinaryExtension(validPath)) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);\n }\n const probe = await readProbe(handle, normalized.signal);\n // A UTF-16 BOM passes the binary probe (it is text), but every reader\n // here (read and edit) decodes UTF-8 and would hand back U+FFFD and NULs\n // as content.\n if (hasUtf16Bom(probe)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'UTF-16 text file detected; convert it to UTF-8 first.',\n filePath,\n );\n }\n if (isBinarySample(probe)) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);\n }\n}\n\nfunction assertSizeWithinLimit(size: number, maxSize: number, filePath: string): void {\n if (size <= maxSize) return;\n throw new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (${size} > ${maxSize} bytes). Use head to preview.`,\n filePath,\n );\n}\n\nasync function readByMode(\n handle: FileHandle,\n validPath: string,\n filePath: string,\n stats: Stats,\n spec: ReadSpec,\n options: ReadOptions,\n): Promise<ReadFileResult> {\n switch (spec.kind) {\n case 'full': {\n assertSizeWithinLimit(stats.size, options.maxSize, filePath);\n const { content, totalLines } = await readFullContent(\n handle,\n options.maxSize,\n filePath,\n options.signal,\n spec.strictUtf8 === true,\n );\n return {\n path: validPath,\n readMode: 'full',\n modified: stats.mtime.toISOString(),\n content,\n totalLines,\n linesRead: totalLines,\n hasMoreLines: false,\n };\n }\n case 'head':\n return {\n path: validPath,\n readMode: 'head',\n modified: stats.mtime.toISOString(),\n head: spec.lines,\n ...(await readRangeContent(handle, 1, spec.lines, options, filePath)),\n };\n case 'range':\n return {\n path: validPath,\n readMode: 'range',\n modified: stats.mtime.toISOString(),\n startLine: spec.start,\n ...(spec.end !== undefined ? { endLine: spec.end } : {}),\n ...(await readRangeContent(handle, spec.start, spec.end, options, filePath)),\n };\n case 'tail':\n return {\n path: validPath,\n readMode: 'tail',\n modified: stats.mtime.toISOString(),\n tail: spec.lines,\n ...(await readTailContent(handle, spec.lines, options, validPath)),\n };\n }\n}\n\nexport function assertFileStats(filePath: string, stats: Stats): void {\n if (!stats.isFile()) {\n throw new FsError(ErrorCode.NOT_FILE, 'Not a regular file', filePath);\n }\n}\n\n/** Read an already-validated, already-stat'd file per `spec`. */\nexport async function readFileWithStats(\n filePath: string,\n validPath: string,\n stats: Stats,\n spec: ReadSpec,\n): Promise<ReadFileResult> {\n const options: ReadOptions = {\n maxSize: getMaxTextFileSize(),\n ...(spec.signal ? { signal: spec.signal } : {}),\n };\n spec.signal?.throwIfAborted();\n assertFileStats(filePath, stats);\n\n await using handle = await openReadableFileHandle(validPath, spec.signal);\n\n await assertNotBinary(validPath, filePath, handle, options);\n spec.signal?.throwIfAborted();\n\n return await readByMode(handle, validPath, filePath, stats, spec, options);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"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","sourcesContent":["// File reading pipeline split out of fs.ts. fs.ts imports from here; never the reverse.\nimport { isUtf8 } from 'node:buffer';\nimport { constants } from 'node:fs';\nimport type { Stats } from 'node:fs';\nimport { open as fsOpen } from 'node:fs/promises';\nimport type { FileHandle } from 'node:fs/promises';\nimport { StringDecoder } from 'node:string_decoder';\n\nimport { withAbort } from './concurrency.ts';\nimport { ErrorCode, formatUnknownErrorMessage, FsError, isFsError } from './errors.ts';\nimport { hasUtf16Bom, isBinarySample, isKnownBinaryExtension, MIME_SAMPLE_SIZE } from './mime.ts';\nimport { Logger } from './observability.ts';\nimport { getMaxTextFileSize } from './util.ts';\n\nconst STREAM_CHUNK_SIZE = 64 * 1024;\n\n// O_NOFOLLOW refuses (ELOOP) a final-component symlink, so a regular file\n// swapped for a symlink between validation and open is refused, not followed.\n// Opens receive the realpath-resolved target, so in-root symlinks still work.\n// Absent (undefined at runtime, despite @types/node) on Windows; 0 is a no-op.\nconst { O_NOFOLLOW = 0, O_NONBLOCK = 0 } = constants as {\n O_NOFOLLOW?: number;\n O_NONBLOCK?: number;\n};\nexport const READ_NOFOLLOW = constants.O_RDONLY | O_NOFOLLOW;\n// O_NONBLOCK: an append to a symlink-swapped FIFO fails fast instead of hanging.\nexport const APPEND_NOFOLLOW = constants.O_WRONLY | constants.O_APPEND | O_NOFOLLOW | O_NONBLOCK;\nexport const APPEND_CREATE_NOFOLLOW =\n constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL | O_NOFOLLOW;\n\nasync function openReadableFileHandle(filePath: string, signal?: AbortSignal): Promise<FileHandle> {\n const handlePromise = fsOpen(filePath, READ_NOFOLLOW);\n if (!signal) return handlePromise;\n try {\n return await withAbort(handlePromise, signal);\n } catch (error) {\n void handlePromise\n .then((handle) => {\n void handle.close().catch((closeErr: unknown) => {\n Logger.warn(\n `Failed to close file handle for ${filePath} after abort: ${formatUnknownErrorMessage(closeErr)}`,\n );\n });\n })\n .catch(() => {\n /* ignore open error */\n });\n throw error;\n }\n}\n\nasync function readProbe(handle: FileHandle, signal?: AbortSignal): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe(MIME_SAMPLE_SIZE);\n const { bytesRead } = await withAbort(handle.read(buffer, 0, MIME_SAMPLE_SIZE, 0), signal);\n\n if (bytesRead === 0) {\n return Buffer.alloc(0);\n }\n\n return buffer.subarray(0, bytesRead);\n}\n\nexport type ReadSpec =\n | { kind: 'full'; signal?: AbortSignal; strictUtf8?: boolean }\n | { kind: 'head'; lines: number; signal?: AbortSignal }\n | { kind: 'tail'; lines: number; signal?: AbortSignal }\n | { kind: 'range'; start: number; end?: number; signal?: AbortSignal };\n\ninterface ReadOptions {\n maxSize: number;\n signal?: AbortSignal;\n}\n\ninterface PartialReadResult {\n content: string;\n linesRead: number;\n hasMoreLines: boolean;\n}\n\nexport interface ReadFileResult {\n path: string;\n content: string;\n totalLines?: number;\n readMode: ReadSpec['kind'];\n modified: string;\n head?: number;\n tail?: number;\n startLine?: number;\n endLine?: number;\n linesRead?: number;\n hasMoreLines?: boolean;\n}\n\nexport function createTooLargeError(\n bytesRead: number,\n maxSize: number,\n requestedPath: string,\n): FsError {\n return new FsError(\n ErrorCode.TOO_LARGE,\n `File exceeds size limit (${bytesRead} > ${maxSize} bytes)`,\n requestedPath,\n );\n}\n\nexport async function readFileBufferWithLimit(\n handle: FileHandle,\n maxSize: number,\n requestedPath: string,\n signal?: AbortSignal,\n): Promise<Buffer> {\n const stream = handle.createReadStream({\n start: 0,\n highWaterMark: STREAM_CHUNK_SIZE,\n autoClose: false,\n emitClose: false,\n signal,\n });\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n try {\n for await (const chunk of stream) {\n const buffer = chunk as Buffer;\n totalSize += buffer.length;\n\n if (totalSize > maxSize) {\n stream.destroy();\n throw createTooLargeError(totalSize, maxSize, requestedPath);\n }\n\n chunks.push(buffer);\n }\n } finally {\n if (!stream.destroyed) {\n stream.on('error', (_err: unknown) => {\n /* suppress post-destroy error event */\n });\n stream.destroy();\n }\n }\n\n return Buffer.concat(chunks, totalSize);\n}\n\nexport const countLines = (content: string): number => {\n if (content.length === 0) return 0;\n const trimmed = content.endsWith('\\n') ? content.slice(0, -1) : content;\n return trimmed.split('\\n').length;\n};\n\nasync function peekHasMore(iterator: AsyncIterator<string>): Promise<boolean> {\n try {\n const { done } = await iterator.next();\n return !done;\n } catch (error) {\n // The peek only asks \"is there another line\", and the caller keeps nothing\n // it returns. An over-long line past the requested range still answers yes\n // — failing the whole read over a line outside it would reject e.g. lines\n // 1-2 of a file whose line 3 is one 50 KB blob.\n if (isFsError(error) && error.code === ErrorCode.TOO_LARGE) return true;\n throw error;\n }\n}\n\nconst LF = 0x0a;\n\n/**\n * Line iterator bounded by bytes rather than by the decoded string, so a file\n * that is one enormous line cannot be materialized before the size check runs.\n * `handle.readLines` decodes the whole line first and can OOM on such a file.\n *\n * Yields lines from `startLine` onward, 1-indexed. Lines before `startLine` are\n * counted and discarded without accumulating, so an over-long line the caller\n * skipped past does not fail the read. Line endings match `readLines`: split on\n * `\\n`, with a preceding `\\r` stripped, so CRLF input decodes identically.\n */\nasync function* readLinesBounded(\n handle: FileHandle,\n options: ReadOptions,\n filePath: string,\n startLine: number,\n): AsyncGenerator<string, void, undefined> {\n const stream = handle.createReadStream({\n start: 0,\n highWaterMark: STREAM_CHUNK_SIZE,\n autoClose: false,\n emitClose: false,\n ...(options.signal ? { signal: options.signal } : {}),\n });\n\n const decoder = new StringDecoder('utf-8');\n let pending = '';\n let pendingBytes = 0;\n let lineNumber = 0;\n\n const tooLong = (bytes: number): FsError =>\n new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (single line ${bytes} > ${options.maxSize} bytes). Use a narrower range or head.`,\n filePath,\n );\n\n try {\n for await (const chunk of stream) {\n const buffer = chunk as Buffer;\n let offset = 0;\n\n // Search loop: each iteration finds the next LF and advances offset past\n // it. Written as while-with-assignment so the re-search is visible, rather\n // than buried in a for \"increment\" slot that re-runs indexOf.\n let nl = buffer.indexOf(LF, offset);\n while (nl !== -1) {\n const segment = buffer.subarray(offset, nl);\n const lineBytes = pendingBytes + segment.length;\n lineNumber++;\n // Decode even when skipping: the decoder carries multi-byte\n // continuation state into the first line the caller keeps.\n const decoded = decoder.write(segment);\n if (lineNumber >= startLine) {\n if (lineBytes > options.maxSize) throw tooLong(lineBytes);\n yield (pending + decoded).replace(/\\r$/, '');\n }\n pending = '';\n pendingBytes = 0;\n offset = nl + 1;\n nl = buffer.indexOf(LF, offset);\n }\n\n const rest = buffer.subarray(offset);\n pendingBytes += rest.length;\n if (lineNumber + 1 < startLine) {\n decoder.write(rest); // still skipping; drop the text, keep decoder state\n } else {\n if (pendingBytes > options.maxSize) throw tooLong(pendingBytes);\n pending += decoder.write(rest);\n }\n }\n\n // Trailing line with no final newline. A file ending in `\\n` leaves\n // pendingBytes at 0 and emits nothing, matching readLines.\n if (pendingBytes > 0) {\n lineNumber++;\n if (lineNumber >= startLine) {\n if (pendingBytes > options.maxSize) throw tooLong(pendingBytes);\n yield pending + decoder.end();\n }\n }\n } finally {\n if (!stream.destroyed) {\n stream.on('error', (_err: unknown) => {\n /* suppress post-destroy error event */\n });\n stream.destroy();\n }\n }\n}\n\nasync function readRangeContent(\n handle: FileHandle,\n startLine: number,\n endLine: number | undefined,\n options: ReadOptions,\n filePath: string,\n): Promise<PartialReadResult> {\n options.signal?.throwIfAborted();\n\n const lines: string[] = [];\n let lineNumber = startLine - 1;\n let estimatedBytes = 0;\n const newlineBytes = Buffer.byteLength('\\n', 'utf-8');\n const stopAt = endLine ?? Number.POSITIVE_INFINITY;\n\n let hasMoreLines = false;\n\n // Byte-bounded: an over-long line throws TOO_LARGE while still a partial\n // buffer, before it can be decoded into one huge string.\n const iterator = readLinesBounded(handle, options, filePath, startLine)[Symbol.asyncIterator]();\n\n try {\n for (;;) {\n const { value: line, done } = await iterator.next();\n if (done) {\n break;\n }\n lineNumber++;\n\n if (lineNumber > stopAt) {\n hasMoreLines = true;\n break;\n }\n\n lines.push(line);\n\n estimatedBytes += Buffer.byteLength(line, 'utf-8') + newlineBytes;\n if (estimatedBytes > options.maxSize) {\n hasMoreLines = await peekHasMore(iterator);\n break;\n }\n\n if (lineNumber === stopAt) {\n hasMoreLines = await peekHasMore(iterator);\n break;\n }\n }\n } finally {\n await iterator.return();\n }\n\n return {\n content: lines.join('\\n'),\n linesRead: lines.length,\n hasMoreLines,\n };\n}\n\nasync function readTailContent(\n handle: FileHandle,\n tail: number,\n options: ReadOptions,\n filePath: string,\n): Promise<PartialReadResult> {\n options.signal?.throwIfAborted();\n\n const stats = await handle.stat();\n const fileSize = stats.size;\n if (fileSize === 0) {\n return {\n content: '',\n linesRead: 0,\n hasMoreLines: false,\n };\n }\n\n const encoding = 'utf-8';\n // Accumulate raw bytes from the end: decoding a chunk ending mid-codepoint\n // corrupts the trailing UTF-8 sequence into U+FFFD. 0x0A is single-byte and\n // never part of a multibyte sequence, so count newlines on the raw buffer and\n // decode the whole bounded buffer once at the end.\n let position = fileSize;\n const chunks: Buffer[] = [];\n let totalLen = 0;\n let newlines = 0;\n let stoppedByLimit = false;\n\n while (position > 0) {\n options.signal?.throwIfAborted();\n const chunkSize = Math.min(position, STREAM_CHUNK_SIZE);\n const buffer = Buffer.allocUnsafe(chunkSize);\n const { bytesRead } = await handle.read(buffer, 0, chunkSize, position - chunkSize);\n if (bytesRead < chunkSize) {\n // The file shrank under us. Bytes from `position` on are still a\n // contiguous suffix, so keep those and stop — splicing this short chunk in\n // would leave a hole (and, with an alloc'd buffer, NULs) mid-content.\n // `position` stays put, so the result is reported as having more lines.\n break;\n }\n position -= chunkSize;\n\n for (const byte of buffer) {\n if (byte === 0x0a) newlines++;\n }\n\n chunks.unshift(buffer);\n totalLen += chunkSize;\n\n // One newline more than `tail`: the oldest segment in the buffer starts\n // mid-line whenever we stop before the file start, and gets dropped below.\n if (newlines > tail) break;\n if (totalLen > options.maxSize) {\n stoppedByLimit = true;\n break;\n }\n }\n\n const allBytes = Buffer.concat(chunks, totalLen);\n let lines = allBytes.toString(encoding).split('\\n');\n\n // A trailing newline produces a spurious empty final segment; drop it.\n if (lines.length > 0 && lines[lines.length - 1] === '') {\n lines = lines.slice(0, -1);\n }\n // Normalize CRLF.\n lines = lines.map((l) => (l.endsWith('\\r') ? l.slice(0, -1) : l));\n\n // We stopped before the file start, so the first segment is the tail of a\n // line whose beginning was never read. Drop it instead of reporting a\n // fragment as a complete line.\n if (position > 0 && lines.length > 0) {\n lines = lines.slice(1);\n }\n\n if (stoppedByLimit && lines.length < tail) {\n throw new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (${totalLen} > ${options.maxSize} bytes, could not collect ${tail} lines). Use a narrower tail or head.`,\n filePath,\n );\n }\n\n let hasMoreLines = stoppedByLimit || position > 0;\n if (lines.length > tail) {\n hasMoreLines = true;\n lines = lines.slice(lines.length - tail);\n }\n\n const content = lines.join('\\n');\n return {\n content,\n linesRead: lines.length,\n hasMoreLines,\n };\n}\n\nasync function readFullContent(\n handle: FileHandle,\n maxSize: number,\n requestedPath: string,\n signal: AbortSignal | undefined,\n strictUtf8: boolean,\n): Promise<{ content: string; totalLines: number }> {\n const buffer = await readFileBufferWithLimit(handle, maxSize, requestedPath, signal);\n // The binary probe samples only the first 512 bytes. A caller that writes the\n // text back must refuse any byte the decode would replace with U+FFFD.\n if (strictUtf8 && (!isUtf8(buffer) || buffer.includes(0))) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary or non-UTF-8 file detected.', requestedPath);\n }\n const content = buffer.toString('utf-8');\n return { content, totalLines: countLines(content) };\n}\n\nasync function assertNotBinary(\n validPath: string,\n filePath: string,\n handle: FileHandle,\n normalized: ReadOptions,\n): Promise<void> {\n normalized.signal?.throwIfAborted();\n if (isKnownBinaryExtension(validPath)) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);\n }\n const probe = await readProbe(handle, normalized.signal);\n // A UTF-16 BOM passes the binary probe (it is text), but every reader\n // here (read and edit) decodes UTF-8 and would hand back U+FFFD and NULs\n // as content.\n if (hasUtf16Bom(probe)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'UTF-16 text file detected; convert it to UTF-8 first.',\n filePath,\n );\n }\n if (isBinarySample(probe)) {\n throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);\n }\n}\n\nfunction assertSizeWithinLimit(size: number, maxSize: number, filePath: string): void {\n if (size <= maxSize) return;\n throw new FsError(\n ErrorCode.TOO_LARGE,\n `File too large (${size} > ${maxSize} bytes). Use head to preview.`,\n filePath,\n );\n}\n\nasync function readByMode(\n handle: FileHandle,\n validPath: string,\n filePath: string,\n stats: Stats,\n spec: ReadSpec,\n options: ReadOptions,\n): Promise<ReadFileResult> {\n switch (spec.kind) {\n case 'full': {\n assertSizeWithinLimit(stats.size, options.maxSize, filePath);\n const { content, totalLines } = await readFullContent(\n handle,\n options.maxSize,\n filePath,\n options.signal,\n spec.strictUtf8 === true,\n );\n return {\n path: validPath,\n readMode: 'full',\n modified: stats.mtime.toISOString(),\n content,\n totalLines,\n linesRead: totalLines,\n hasMoreLines: false,\n };\n }\n case 'head':\n return {\n path: validPath,\n readMode: 'head',\n modified: stats.mtime.toISOString(),\n head: spec.lines,\n ...(await readRangeContent(handle, 1, spec.lines, options, filePath)),\n };\n case 'range':\n return {\n path: validPath,\n readMode: 'range',\n modified: stats.mtime.toISOString(),\n startLine: spec.start,\n ...(spec.end !== undefined ? { endLine: spec.end } : {}),\n ...(await readRangeContent(handle, spec.start, spec.end, options, filePath)),\n };\n case 'tail':\n return {\n path: validPath,\n readMode: 'tail',\n modified: stats.mtime.toISOString(),\n tail: spec.lines,\n ...(await readTailContent(handle, spec.lines, options, validPath)),\n };\n }\n}\n\nexport function assertFileStats(filePath: string, stats: Stats): void {\n if (!stats.isFile()) {\n throw new FsError(ErrorCode.NOT_FILE, 'Not a regular file', filePath);\n }\n}\n\n/** Read an already-validated, already-stat'd file per `spec`. */\nexport async function readFileWithStats(\n filePath: string,\n validPath: string,\n stats: Stats,\n spec: ReadSpec,\n verifyHandle?: (handle: FileHandle) => Promise<void>,\n): Promise<ReadFileResult> {\n const options: ReadOptions = {\n maxSize: getMaxTextFileSize(),\n ...(spec.signal ? { signal: spec.signal } : {}),\n };\n spec.signal?.throwIfAborted();\n assertFileStats(filePath, stats);\n\n await using handle = await openReadableFileHandle(validPath, spec.signal);\n await verifyHandle?.(handle);\n\n await assertNotBinary(validPath, filePath, handle, options);\n spec.signal?.throwIfAborted();\n\n return await readByMode(handle, validPath, filePath, stats, spec, options);\n}\n"]}
|
package/dist/core/search.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ interface SearchResult {
|
|
|
12
12
|
after?: string[];
|
|
13
13
|
}
|
|
14
14
|
export type Regex = RE2;
|
|
15
|
+
/**
|
|
16
|
+
* re2-wasm copies the whole input on every exec, so draining a dense pattern is
|
|
17
|
+
* O(matches x input) and fully synchronous: the loop pins and the tool's
|
|
18
|
+
* AbortSignal.timeout (a timer) can never fire. The returned `tick` yields a
|
|
19
|
+
* macrotask every ~10ms and reports whether `signal` has aborted.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createYielder(signal: AbortSignal | undefined): () => Promise<boolean>;
|
|
15
22
|
/**
|
|
16
23
|
* Largest input one RE2 call may scan as a single string. The input is copied
|
|
17
24
|
* into the same fixed 16 MB wasm heap as the patterns; about 10 MB of input
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAIxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAU3C,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,KAAK,GAAG,GAAG,CAAC;AAKxB;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAU,CAAC;AAO3C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7D,KAAK,CAWP;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,IAAI,CAOxD;AAED,MAAM,WAAW,UAAU;IACzB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAC/B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAiB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,CA2BzF;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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"}
|
package/dist/core/search.js
CHANGED
|
@@ -12,6 +12,24 @@ import { escapeRegExp, getMaxTextFileSize, MIB, PARALLEL_CONCURRENCY } from "./u
|
|
|
12
12
|
const own = (s) => Buffer.from(s, 'utf8').toString('utf8');
|
|
13
13
|
/** Occurrence-counting bound, so one pathological line cannot spin forever. */
|
|
14
14
|
const MAX_MATCHES_PER_LINE = 100_000;
|
|
15
|
+
/**
|
|
16
|
+
* re2-wasm copies the whole input on every exec, so draining a dense pattern is
|
|
17
|
+
* O(matches x input) and fully synchronous: the loop pins and the tool's
|
|
18
|
+
* AbortSignal.timeout (a timer) can never fire. The returned `tick` yields a
|
|
19
|
+
* macrotask every ~10ms and reports whether `signal` has aborted.
|
|
20
|
+
*/
|
|
21
|
+
export function createYielder(signal) {
|
|
22
|
+
let last = Date.now();
|
|
23
|
+
return async () => {
|
|
24
|
+
if (Date.now() - last > 10) {
|
|
25
|
+
await new Promise((resolve) => {
|
|
26
|
+
setImmediate(resolve);
|
|
27
|
+
});
|
|
28
|
+
last = Date.now();
|
|
29
|
+
}
|
|
30
|
+
return signal?.aborted ?? false;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
15
33
|
/**
|
|
16
34
|
* Largest input one RE2 call may scan as a single string. The input is copied
|
|
17
35
|
* into the same fixed 16 MB wasm heap as the patterns; about 10 MB of input
|
|
@@ -122,14 +140,16 @@ export function* execMatches(regex, text) {
|
|
|
122
140
|
* Find non-overlapping occurrences of a global regex in a single line, reporting
|
|
123
141
|
* the first match's column alongside the count.
|
|
124
142
|
*/
|
|
125
|
-
function findLineMatches(regex, line) {
|
|
143
|
+
async function findLineMatches(regex, line, tick) {
|
|
126
144
|
let count = 0;
|
|
127
145
|
let column = -1;
|
|
146
|
+
// Awaiting mid-drain is safe with a shared regex: execMatches re-writes
|
|
147
|
+
// lastIndex from its own match before every exec, with no await inside.
|
|
128
148
|
for (const match of execMatches(regex, line)) {
|
|
129
149
|
if (column === -1)
|
|
130
150
|
column = match.start;
|
|
131
151
|
count++;
|
|
132
|
-
if (count >= MAX_MATCHES_PER_LINE)
|
|
152
|
+
if (count >= MAX_MATCHES_PER_LINE || (await tick()))
|
|
133
153
|
break;
|
|
134
154
|
}
|
|
135
155
|
return count > 0 ? { count, column } : undefined;
|
|
@@ -174,14 +194,25 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
174
194
|
// empty file splits into one empty element: neither is a line the
|
|
175
195
|
// file has, for matching or for context.
|
|
176
196
|
const lineCount = content.length === 0 ? 0 : content.endsWith('\n') ? lines.length - 1 : lines.length;
|
|
197
|
+
// A single line past RE2_MAX_INPUT_BYTES OOMs the wasm module for the rest of
|
|
198
|
+
// the process; report the file tooLarge instead. Only a buffer over the bound
|
|
199
|
+
// can hold such a line, so ordinary files skip the per-line measure.
|
|
200
|
+
const checkLineSize = buffer.length > RE2_MAX_INPUT_BYTES;
|
|
177
201
|
const matches = [];
|
|
202
|
+
const tick = createYielder(scanCtx.signal);
|
|
178
203
|
for (let i = 0; i < lineCount; i++) {
|
|
179
204
|
const line = lines[i];
|
|
180
205
|
if (line === undefined)
|
|
181
206
|
continue;
|
|
207
|
+
if (checkLineSize && Buffer.byteLength(line, 'utf8') > RE2_MAX_INPUT_BYTES) {
|
|
208
|
+
return { kind: 'tooLarge' };
|
|
209
|
+
}
|
|
210
|
+
// Yield between lines too: millions of short lines never yield per-match.
|
|
211
|
+
if (await tick())
|
|
212
|
+
break;
|
|
182
213
|
// One scan per line: the helper resets lastIndex itself, so it
|
|
183
214
|
// doubles as the "does this line match" test.
|
|
184
|
-
const found = findLineMatches(scanCtx.regex, line);
|
|
215
|
+
const found = await findLineMatches(scanCtx.regex, line, tick);
|
|
185
216
|
if (found) {
|
|
186
217
|
matches.push({
|
|
187
218
|
file: entryPath,
|
|
@@ -204,19 +235,23 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
204
235
|
}
|
|
205
236
|
export async function searchContent(directory, pattern, options, pathGuard) {
|
|
206
237
|
const source = options.isRegex ? pattern || '' : escapeRegExp(pattern || '');
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// buffer hit (the converse can be false — a false positive just falls through
|
|
212
|
-
// to the per-line scan). \A/\z/\Z are buffer-relative even under `m`, inline
|
|
213
|
-
// flag groups can undo `m`, and $ cannot see past a \r, so those inputs skip
|
|
214
|
-
// the prefilter (see canPrefilter).
|
|
215
|
-
const prefilter = compileRegex(source, {
|
|
216
|
-
caseSensitive: Boolean(options.caseSensitive),
|
|
217
|
-
multiline: true,
|
|
218
|
-
});
|
|
238
|
+
// Compiles live inside the try so a failing second compile does not leak the
|
|
239
|
+
// first's wasm memory; freeRegex is null-safe.
|
|
240
|
+
let regex;
|
|
241
|
+
let prefilter;
|
|
219
242
|
try {
|
|
243
|
+
regex = compileRegex(source, {
|
|
244
|
+
caseSensitive: Boolean(options.caseSensitive),
|
|
245
|
+
});
|
|
246
|
+
// Whole-buffer test: `m` makes ^/$ line-relative so a per-line hit is always a
|
|
247
|
+
// buffer hit (the converse can be false — a false positive just falls through
|
|
248
|
+
// to the per-line scan). \A/\z/\Z are buffer-relative even under `m`, inline
|
|
249
|
+
// flag groups can undo `m`, and $ cannot see past a \r, so those inputs skip
|
|
250
|
+
// the prefilter (see canPrefilter).
|
|
251
|
+
prefilter = compileRegex(source, {
|
|
252
|
+
caseSensitive: Boolean(options.caseSensitive),
|
|
253
|
+
multiline: true,
|
|
254
|
+
});
|
|
220
255
|
const matches = [];
|
|
221
256
|
const maxResults = options.maxResults ?? 100;
|
|
222
257
|
const maxFileSize = getMaxTextFileSize();
|
package/dist/core/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat as fsStat, readFile } from 'node:fs/promises';\nimport { basename } 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 { globEntries, type GlobEntry } 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 * 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\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 */\nfunction findLineMatches(\n regex: Regex,\n line: string,\n): { count: number; column: number } | undefined {\n let count = 0;\n let column = -1;\n for (const match of execMatches(regex, line)) {\n if (column === -1) column = match.start;\n count++;\n if (count >= MAX_MATCHES_PER_LINE) 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: 'inaccessible' }\n | { kind: 'tooLarge' }\n | { kind: 'binary' }\n | { kind: 'scanned'; matches: SearchResult[] };\n\ninterface ScanContext {\n pathGuard: PathGuard;\n signal: AbortSignal | undefined;\n maxFileSize: number;\n maxResults: number;\n regex: Regex;\n prefilter: Regex;\n source: string;\n context: number;\n}\n\nasync function scanFile(entryPath: string, scanCtx: ScanContext): Promise<FileScan> {\n try {\n await scanCtx.pathGuard.validateExistingPath(entryPath);\n } catch {\n return { kind: 'inaccessible' };\n }\n\n try {\n const stats = await fsStat(entryPath);\n if (stats.size > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n } catch {\n return { kind: 'inaccessible' };\n }\n\n let buffer: Buffer;\n try {\n buffer = await readFile(entryPath, { signal: scanCtx.signal });\n } catch (error) {\n if (scanCtx.signal?.aborted) throw error;\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 content = buffer.toString('utf-8');\n if (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 const matches: SearchResult[] = [];\n for (let i = 0; i < lineCount; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n // One scan per line: the helper resets lastIndex itself, so it\n // doubles as the \"does this line match\" test.\n const found = findLineMatches(scanCtx.regex, line);\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 const 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 const prefilter = compileRegex(source, {\n caseSensitive: Boolean(options.caseSensitive),\n multiline: true,\n });\n try {\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 pathGuard,\n signal: options.signal,\n maxFileSize,\n maxResults,\n regex,\n prefilter,\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 '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\nasync function* guardedEntries(\n entries: AsyncIterable<GlobEntry>,\n pathGuard: PathGuard,\n signal: AbortSignal | undefined,\n counters: { skippedInaccessible: number; stoppedByAbort: boolean },\n): AsyncGenerator<GlobEntry> {\n // The signal carries both client cancellation and the tool's search timeout,\n // so an abort means \"return what we have, marked incomplete\" rather than\n // throw — but it must never be reported as a finished scan. The walk itself\n // throws when the signal stops it before a single entry is yielded.\n try {\n for await (const entry of entries) {\n if (signal?.aborted) {\n counters.stoppedByAbort = true;\n return;\n }\n try {\n await pathGuard.validateExistingPath(entry.path);\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n yield entry;\n }\n } catch (error) {\n if (!signal?.aborted) throw error;\n counters.stoppedByAbort = true;\n }\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 const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push({ path: entry.path });\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(results.length >= maxResults, counters.stoppedByAbort);\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: stoppedReason !== undefined,\n skippedInaccessible: counters.skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat as fsStat, readFile } from 'node:fs/promises';\nimport { basename } 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 { globEntries, type GlobEntry } 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\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<{ count: number; column: number } | 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: 'inaccessible' }\n | { kind: 'tooLarge' }\n | { kind: 'binary' }\n | { kind: 'scanned'; matches: SearchResult[] };\n\ninterface ScanContext {\n pathGuard: PathGuard;\n signal: AbortSignal | undefined;\n maxFileSize: number;\n maxResults: number;\n regex: Regex;\n prefilter: Regex;\n source: string;\n context: number;\n}\n\nasync function scanFile(entryPath: string, scanCtx: ScanContext): Promise<FileScan> {\n try {\n await scanCtx.pathGuard.validateExistingPath(entryPath);\n } catch {\n return { kind: 'inaccessible' };\n }\n\n try {\n const stats = await fsStat(entryPath);\n if (stats.size > scanCtx.maxFileSize) return { kind: 'tooLarge' };\n } catch {\n return { kind: 'inaccessible' };\n }\n\n let buffer: Buffer;\n try {\n buffer = await readFile(entryPath, { signal: scanCtx.signal });\n } catch (error) {\n if (scanCtx.signal?.aborted) throw error;\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 content = buffer.toString('utf-8');\n if (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.\n const checkLineSize = 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 = 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 pathGuard,\n signal: options.signal,\n maxFileSize,\n maxResults,\n regex,\n prefilter,\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 '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\nasync function* guardedEntries(\n entries: AsyncIterable<GlobEntry>,\n pathGuard: PathGuard,\n signal: AbortSignal | undefined,\n counters: { skippedInaccessible: number; stoppedByAbort: boolean },\n): AsyncGenerator<GlobEntry> {\n // The signal carries both client cancellation and the tool's search timeout,\n // so an abort means \"return what we have, marked incomplete\" rather than\n // throw — but it must never be reported as a finished scan. The walk itself\n // throws when the signal stops it before a single entry is yielded.\n try {\n for await (const entry of entries) {\n if (signal?.aborted) {\n counters.stoppedByAbort = true;\n return;\n }\n try {\n await pathGuard.validateExistingPath(entry.path);\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n yield entry;\n }\n } catch (error) {\n if (!signal?.aborted) throw error;\n counters.stoppedByAbort = true;\n }\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 const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push({ path: entry.path });\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(results.length >= maxResults, counters.stoppedByAbort);\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: stoppedReason !== undefined,\n skippedInaccessible: counters.skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n"]}
|
package/dist/core/util.d.ts
CHANGED
|
@@ -36,6 +36,14 @@ export declare function getMaxTextFileSize(): number;
|
|
|
36
36
|
export declare function getMaxInboundMessageBytes(): number;
|
|
37
37
|
/** Combined byte budget for one batched `read`. */
|
|
38
38
|
export declare const READ_MANY_MAX_TOTAL_BYTES: number;
|
|
39
|
+
/**
|
|
40
|
+
* Ceiling on the JSON-encoded size of one `read` result. The MCP SDK's stdio
|
|
41
|
+
* transport caps a message at a fixed 10 MiB ReadBuffer regardless of
|
|
42
|
+
* `--max-file-size`, so a file under the size limit can still overflow it once
|
|
43
|
+
* escaped; `read` returns TOO_LARGE instead. ~256 KiB covers the JSON-RPC
|
|
44
|
+
* envelope this measure omits.
|
|
45
|
+
*/
|
|
46
|
+
export declare const MAX_OUTBOUND_RESULT_BYTES: number;
|
|
39
47
|
/** Default line chunk size for read continuation when no explicit range was given. */
|
|
40
48
|
export declare const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;
|
|
41
49
|
export declare const DEFAULT_SEARCH_TIMEOUT_MS = 5000;
|
package/dist/core/util.d.ts.map
CHANGED
|
@@ -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;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,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,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;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;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,QAAuB,CAAC;AAE9D,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,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
|
@@ -104,6 +104,14 @@ export function getMaxInboundMessageBytes() {
|
|
|
104
104
|
}
|
|
105
105
|
/** Combined byte budget for one batched `read`. */
|
|
106
106
|
export const READ_MANY_MAX_TOTAL_BYTES = 512 * KIB;
|
|
107
|
+
/**
|
|
108
|
+
* Ceiling on the JSON-encoded size of one `read` result. The MCP SDK's stdio
|
|
109
|
+
* transport caps a message at a fixed 10 MiB ReadBuffer regardless of
|
|
110
|
+
* `--max-file-size`, so a file under the size limit can still overflow it once
|
|
111
|
+
* escaped; `read` returns TOO_LARGE instead. ~256 KiB covers the JSON-RPC
|
|
112
|
+
* envelope this measure omits.
|
|
113
|
+
*/
|
|
114
|
+
export const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;
|
|
107
115
|
/** Default line chunk size for read continuation when no explicit range was given. */
|
|
108
116
|
export const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;
|
|
109
117
|
export const DEFAULT_SEARCH_TIMEOUT_MS = 5000;
|
package/dist/core/util.js.map
CHANGED
|
@@ -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\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/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\nexport const DEFAULT_SEARCH_TIMEOUT_MS = 5000;\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":"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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\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 the JSON-encoded size of one `read` result. The MCP SDK's stdio\n * transport caps a message at a fixed 10 MiB ReadBuffer regardless of\n * `--max-file-size`, so a file under the size limit can still overflow it once\n * escaped; `read` returns TOO_LARGE instead. ~256 KiB covers the JSON-RPC\n * envelope this measure omits.\n */\nexport const MAX_OUTBOUND_RESULT_BYTES = 10 * MIB - 256 * KIB;\n\n/** Default line chunk size for read continuation when no explicit range was given. */\nexport const DEFAULT_CONTINUATION_CHUNK_SIZE = 200;\n\nexport const DEFAULT_SEARCH_TIMEOUT_MS = 5000;\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"]}
|
package/dist/server.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export declare function createServer(options?: ServerOptions, extraDeps?: {
|
|
|
36
36
|
pageStore?: PageSnapshotStore;
|
|
37
37
|
/** The protocol era this instance serves; omitted where the caller does not know. */
|
|
38
38
|
era?: 'legacy' | 'modern';
|
|
39
|
+
/** Only `stdio` has the SDK's fixed 10 MiB ReadBuffer, so only there does `read` cap its result. */
|
|
40
|
+
transport?: 'stdio' | 'http';
|
|
39
41
|
/**
|
|
40
42
|
* The resolved HTTP credential, from `--api-key` or `FS_API_KEY`. Lives here
|
|
41
43
|
* rather than on `ServerOptions` because that object is `PathGuard`'s
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAgBlE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,mBAAmB,IAAI,IAAI,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,OAAO,GAAE,aAAkB,EAC3B,SAAS,CAAC,EAAE;IACV,0EAA0E;IAC1E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qFAAqF;IACrF,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAgBlE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,mBAAmB,IAAI,IAAI,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,OAAO,GAAE,aAAkB,EAC3B,SAAS,CAAC,EAAE;IACV,0EAA0E;IAC1E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qFAAqF;IACrF,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,oGAAoG;IACpG,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,uBAAuB,CAAC,CA8IlC"}
|
package/dist/server.js
CHANGED
|
@@ -123,6 +123,7 @@ export async function createServer(options = {}, extraDeps) {
|
|
|
123
123
|
...(extraDeps?.watcherRegistry ? { watcherRegistry: extraDeps.watcherRegistry } : {}),
|
|
124
124
|
...(extraDeps?.notifier ? { notifier: extraDeps.notifier } : {}),
|
|
125
125
|
...(extraDeps?.era ? { era: extraDeps.era } : {}),
|
|
126
|
+
...(extraDeps?.transport ? { transport: extraDeps.transport } : {}),
|
|
126
127
|
};
|
|
127
128
|
const resourceDisposable = registerResources(deps);
|
|
128
129
|
registerPrompts(deps);
|