@j0hanz/filesystem-mcp 2.8.1 → 2.9.0

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.
Files changed (113) hide show
  1. package/README.md +32 -26
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +10 -1
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +10 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts +4 -4
  9. package/dist/core/concurrency.d.ts.map +1 -1
  10. package/dist/core/concurrency.js +1 -1
  11. package/dist/core/concurrency.js.map +1 -1
  12. package/dist/core/config.d.ts +2 -0
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/file-uri.d.ts +12 -3
  16. package/dist/core/file-uri.d.ts.map +1 -1
  17. package/dist/core/file-uri.js +35 -7
  18. package/dist/core/file-uri.js.map +1 -1
  19. package/dist/core/fmt.d.ts +2 -0
  20. package/dist/core/fmt.d.ts.map +1 -1
  21. package/dist/core/fmt.js +16 -0
  22. package/dist/core/fmt.js.map +1 -1
  23. package/dist/core/fs.d.ts +16 -1
  24. package/dist/core/fs.d.ts.map +1 -1
  25. package/dist/core/fs.js +127 -32
  26. package/dist/core/fs.js.map +1 -1
  27. package/dist/core/glob.d.ts +8 -0
  28. package/dist/core/glob.d.ts.map +1 -1
  29. package/dist/core/glob.js +84 -11
  30. package/dist/core/glob.js.map +1 -1
  31. package/dist/core/path-completer.d.ts.map +1 -1
  32. package/dist/core/path-completer.js +5 -3
  33. package/dist/core/path-completer.js.map +1 -1
  34. package/dist/core/path-utils.d.ts +42 -6
  35. package/dist/core/path-utils.d.ts.map +1 -1
  36. package/dist/core/path-utils.js +56 -10
  37. package/dist/core/path-utils.js.map +1 -1
  38. package/dist/core/path.d.ts +111 -7
  39. package/dist/core/path.d.ts.map +1 -1
  40. package/dist/core/path.js +453 -65
  41. package/dist/core/path.js.map +1 -1
  42. package/dist/core/read.d.ts +6 -0
  43. package/dist/core/read.d.ts.map +1 -1
  44. package/dist/core/read.js +26 -3
  45. package/dist/core/read.js.map +1 -1
  46. package/dist/core/schema.d.ts +9 -6
  47. package/dist/core/schema.d.ts.map +1 -1
  48. package/dist/core/schema.js +22 -19
  49. package/dist/core/schema.js.map +1 -1
  50. package/dist/core/search.d.ts +6 -0
  51. package/dist/core/search.d.ts.map +1 -1
  52. package/dist/core/search.js +159 -33
  53. package/dist/core/search.js.map +1 -1
  54. package/dist/core/sensitive.d.ts.map +1 -1
  55. package/dist/core/sensitive.js +64 -15
  56. package/dist/core/sensitive.js.map +1 -1
  57. package/dist/core/store.d.ts +1 -0
  58. package/dist/core/store.d.ts.map +1 -1
  59. package/dist/core/store.js +3 -0
  60. package/dist/core/store.js.map +1 -1
  61. package/dist/core/util.d.ts +6 -1
  62. package/dist/core/util.d.ts.map +1 -1
  63. package/dist/core/util.js +8 -1
  64. package/dist/core/util.js.map +1 -1
  65. package/dist/resources.d.ts.map +1 -1
  66. package/dist/resources.js +18 -9
  67. package/dist/resources.js.map +1 -1
  68. package/dist/server.js +1 -1
  69. package/dist/server.js.map +1 -1
  70. package/dist/tools/create.d.ts.map +1 -1
  71. package/dist/tools/create.js +121 -43
  72. package/dist/tools/create.js.map +1 -1
  73. package/dist/tools/define.d.ts +8 -4
  74. package/dist/tools/define.d.ts.map +1 -1
  75. package/dist/tools/define.js +36 -9
  76. package/dist/tools/define.js.map +1 -1
  77. package/dist/tools/delete.d.ts.map +1 -1
  78. package/dist/tools/delete.js +50 -11
  79. package/dist/tools/delete.js.map +1 -1
  80. package/dist/tools/diff.js +1 -1
  81. package/dist/tools/diff.js.map +1 -1
  82. package/dist/tools/edit.d.ts.map +1 -1
  83. package/dist/tools/edit.js +14 -12
  84. package/dist/tools/edit.js.map +1 -1
  85. package/dist/tools/find-files.d.ts.map +1 -1
  86. package/dist/tools/find-files.js +15 -9
  87. package/dist/tools/find-files.js.map +1 -1
  88. package/dist/tools/list.d.ts.map +1 -1
  89. package/dist/tools/list.js +138 -86
  90. package/dist/tools/list.js.map +1 -1
  91. package/dist/tools/move.d.ts.map +1 -1
  92. package/dist/tools/move.js +72 -26
  93. package/dist/tools/move.js.map +1 -1
  94. package/dist/tools/patch.js +13 -6
  95. package/dist/tools/patch.js.map +1 -1
  96. package/dist/tools/read.d.ts.map +1 -1
  97. package/dist/tools/read.js +114 -45
  98. package/dist/tools/read.js.map +1 -1
  99. package/dist/tools/replace-text.d.ts.map +1 -1
  100. package/dist/tools/replace-text.js +49 -35
  101. package/dist/tools/replace-text.js.map +1 -1
  102. package/dist/tools/search-text.d.ts.map +1 -1
  103. package/dist/tools/search-text.js +15 -33
  104. package/dist/tools/search-text.js.map +1 -1
  105. package/dist/tools/stat.d.ts.map +1 -1
  106. package/dist/tools/stat.js +26 -8
  107. package/dist/tools/stat.js.map +1 -1
  108. package/dist/transport/http.d.ts.map +1 -1
  109. package/dist/transport/http.js +1 -0
  110. package/dist/transport/http.js.map +1 -1
  111. package/dist/transport/stdio.js +1 -1
  112. package/dist/transport/stdio.js.map +1 -1
  113. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"glob.js","sourceRoot":"","sources":["../../src/core/glob.ts"],"names":[],"mappings":"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","sourcesContent":["import { glob as fsGlob, readFile as fsReadFile, lstat } from 'node:fs/promises';\nimport { basename, dirname, join, matchesGlob, relative, resolve, win32 } from 'node:path';\n\nimport type { Ignore } from 'ignore';\nimport ignore from 'ignore';\n\nimport { processInParallel } from './concurrency.ts';\nimport { formatUnknownErrorMessage } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport type { DirentLike } from './path-utils.ts';\nimport { IS_WINDOWS, isPathInsideDirectory, toPosixPath } from './path-utils.ts';\nimport { MIB } from './util.ts';\n\n/**\n * More brace alternatives than this is not a file filter. fs.glob's matcher\n * dedupes its alternatives pairwise, synchronously: `{1..100000}` pins the\n * event loop for most of a minute, where 1024 costs tens of milliseconds.\n */\nexport const MAX_BRACE_ALTERNATIVES = 1024;\n\n/** Bound synchronous glob compilation when checking candidate traversal segments. */\nexport const MAX_DOTDOT_CHECK_CHARS = 4096;\n\n// The range bodies fs.glob's brace expansion recognises, as it writes them.\nconst NUMERIC_RANGE_RE = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/u;\nconst ALPHA_RANGE_RE = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/u;\n\n/** The values a range body (`x..y`, `x..y..step`) expands to, or `undefined` past the cap. */\nfunction rangeValues(body: string): string[] | undefined {\n const alpha = ALPHA_RANGE_RE.test(body);\n const [from = '', to = '', step = '1'] = body.split('..');\n const bound = (end: string): number => (alpha ? end.charCodeAt(0) : Number.parseInt(end, 10));\n const start = bound(from);\n const stop = bound(to);\n const stride = Math.max(Math.abs(Number.parseInt(step, 10)), 1);\n // Past 2^53 a step no longer moves the number (2^53 + 1 === 2^53), and past\n // Number.MAX_VALUE it reads as Infinity: either way the loop below would\n // never end. Refuse them like any other long range.\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(stop)) return undefined;\n if (Math.floor(Math.abs(stop - start) / stride) >= MAX_BRACE_ALTERNATIVES) return undefined;\n const values: string[] = [];\n const direction = stop < start ? -1 : 1;\n for (let i = start; (stop - i) * direction >= 0; i += stride * direction) {\n // The engine drops a `\\` a letter range passes through (`{Z..a}`), and\n // digits can never spell a root or a `..`, so their padding is moot.\n const char = String.fromCharCode(i);\n values.push(alpha ? (char === '\\\\' ? '' : char) : String(i));\n }\n return values;\n}\n\ninterface BraceSet {\n pre: string;\n /** `undefined` when the set alone expands past the cap. */\n alternatives: string[] | undefined;\n post: string;\n}\n\n/**\n * The leftmost `{…}` that fs.glob would expand: a comma list or a range.\n * The expansion is textual, as fs.glob's is: a brace inside `[…]` still\n * counts (unlike SensitiveMatcher's own glob dialect in sensitive.ts).\n */\nfunction firstBraceSet(pattern: string): BraceSet | undefined {\n const open: { at: number; commas: number[] }[] = [];\n let first: { at: number; commas: number[]; close: number } | undefined;\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === '{') open.push({ at: i, commas: [] });\n else if (char === ',') open.at(-1)?.commas.push(i);\n else if (char === '}') {\n const set = open.pop();\n if (!set || (first && first.at < set.at)) continue;\n const body = pattern.slice(set.at + 1, i);\n if (set.commas.length > 0 || NUMERIC_RANGE_RE.test(body) || ALPHA_RANGE_RE.test(body)) {\n first = { ...set, close: i };\n }\n }\n }\n if (!first) return undefined;\n let alternatives: string[] | undefined;\n if (first.commas.length > 0) {\n alternatives = [];\n let start = first.at + 1;\n for (const end of [...first.commas, first.close]) {\n alternatives.push(pattern.slice(start, end));\n start = end + 1;\n }\n } else {\n alternatives = rangeValues(pattern.slice(first.at + 1, first.close));\n }\n return { pre: pattern.slice(0, first.at), alternatives, post: pattern.slice(first.close + 1) };\n}\n\n/** Every brace alternative of `pattern`, or `undefined` past the cap. */\nfunction expandBraceSets(pattern: string, out: string[] = []): string[] | undefined {\n const set = firstBraceSet(pattern);\n if (!set) {\n out.push(pattern);\n return out.length > MAX_BRACE_ALTERNATIVES ? undefined : out;\n }\n if (!set.alternatives) return undefined;\n for (const alternative of set.alternatives) {\n if (!expandBraceSets(`${set.pre}${alternative}${set.post}`, out)) return undefined;\n }\n return out;\n}\n\n/** The characters minimatch can spell a literal `..` with: dots and one-character classes. */\nconst DOTDOT_SPELLING_RE = /^[.[\\]-]+$/u;\n\n/**\n * Whether the glob engine reads this brace-free segment as `..`. readdir never\n * yields `..`, so only a literal reaches it, and minimatch spells a literal dot\n * as `.`, `[.]` or `[.-.]`. Any other character (`*`, `?`, an extglob, a\n * letter) either makes the segment a pattern the walk never follows to `..`,\n * or puts that character in the name. Only the rest is worth asking the engine\n * about, so a long or exotic segment costs no compile; one the engine cannot\n * compile is refused.\n */\nconst isTraversalSegment = (segment: string): boolean => {\n try {\n return matchesGlob('..', segment);\n } catch {\n return true;\n }\n};\n\n/**\n * An alternative that starts at a root: `/x`, `//host/share`, `C:/x`, `C:x`,\n * on any host. On Windows fs.glob also reads a literal first segment ending in\n * `:` as a drive (`[C]:/x`); elsewhere that is an ordinary name (`logs:`).\n */\nconst isRooted = (alternative: string): boolean =>\n win32.parse(alternative).root !== '' ||\n (IS_WINDOWS && (alternative.split('/')[0] ?? '').endsWith(':'));\n\nexport function isSafeGlobSyntax(pattern: string): boolean {\n if (!pattern || pattern.trim().length === 0) {\n return false;\n }\n // The walk sees the pattern with `\\` as `/` (normalizePattern), so check\n // that form: `\\etc\\x` is `/etc/x` there, an absolute walk on any host.\n // Brace sets can build a root or a `..` from parts (`{/etc,x}`, `..{,x}`,\n // `.{Z..a}.`), so check every alternative, not the pattern as written.\n // globEntries still drops any match outside its cwd: this check is what\n // turns such a pattern into an error rather than a quietly narrower walk.\n const alternatives = expandBraceSets(toPosixPath(pattern));\n if (!alternatives) return false;\n const segments = new Set<string>();\n for (const alternative of alternatives) {\n if (isRooted(alternative)) return false;\n for (const segment of alternative.split('/')) segments.add(segment);\n }\n let dotdotCheckChars = 0;\n for (const segment of segments) {\n if (!segment.includes('.') || !DOTDOT_SPELLING_RE.test(segment)) continue;\n dotdotCheckChars += segment.length;\n if (dotdotCheckChars > MAX_DOTDOT_CHECK_CHARS || isTraversalSegment(segment)) return false;\n }\n return true;\n}\n\n/** A .gitignore past this size is not a .gitignore; skip it rather than read it. */\nconst MAX_GITIGNORE_BYTES = MIB;\n\nasync function loadGitignoreFiles(\n root: string,\n gitignorePaths: readonly string[],\n manager: GitignoreManager,\n signal?: AbortSignal,\n): Promise<void> {\n await processInParallel(\n gitignorePaths,\n async (relPath) => {\n const absPath = join(root, relPath);\n try {\n // lstat, not stat: a symlinked .gitignore may point at a device, a\n // FIFO or a huge file outside the root. Only a plain, small file is a\n // rule set worth reading.\n const info = await lstat(absPath);\n if (!info.isFile() || info.size > MAX_GITIGNORE_BYTES) return;\n const contents = await fsReadFile(absPath, { encoding: 'utf-8', signal });\n const matcher = ignore();\n matcher.add(contents);\n const dir = toPosixPath(dirname(relPath));\n manager.addMatcher(dir === '.' ? '' : dir, matcher);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n Logger.warn(`Failed to read .gitignore at ${absPath}: ${formatUnknownErrorMessage(error)}`);\n }\n },\n GLOB_BATCH_CONCURRENCY,\n signal,\n );\n}\n\n/**\n * Load `.gitignore` from each ancestor of `root` up to and including `ceiling`,\n * farthest first. A rule set that ignores `root` itself is skipped: the caller\n * named that directory on purpose, and applying the rule would empty the walk.\n */\nasync function loadAncestorGitignores(\n root: string,\n ceiling: string,\n manager: GitignoreManager,\n signal?: AbortSignal,\n): Promise<void> {\n const normalizedCeiling = resolve(ceiling);\n const chain: string[] = [];\n for (let dir = dirname(resolve(root)); ; dir = dirname(dir)) {\n if (!isPathInsideDirectory(normalizedCeiling, dir)) break;\n chain.push(dir);\n if (\n (isPathInsideDirectory(dir, normalizedCeiling) &&\n isPathInsideDirectory(normalizedCeiling, dir)) ||\n dirname(dir) === dir\n ) {\n break;\n }\n }\n for (const dir of chain.reverse()) {\n signal?.throwIfAborted();\n const file = join(dir, '.gitignore');\n try {\n const info = await lstat(file);\n if (!info.isFile() || info.size > MAX_GITIGNORE_BYTES) continue;\n const matcher = ignore().add(await fsReadFile(file, { encoding: 'utf-8', signal }));\n const prefix = toPosixPath(relative(dir, root));\n if (matcher.test(`${prefix}/`).ignored) continue;\n manager.addAncestorMatcher(prefix, matcher);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n // ENOENT is the normal case (no .gitignore at this level); anything else is not worth a warning per walk\n }\n }\n}\n\nclass GitignoreManager {\n private matchers = new Map<string, Ignore>();\n /** Rules from directories above the walk root, farthest first; `prefix` re-bases a walk-relative path to that directory. */\n private ancestors: { prefix: string; matcher: Ignore }[] = [];\n\n addMatcher(dir: string, matcher: Ignore): void {\n this.matchers.set(dir, matcher);\n }\n\n addAncestorMatcher(prefix: string, matcher: Ignore): void {\n this.ancestors.push({ prefix, matcher });\n }\n\n /** `null` when no `.gitignore` was found, so callers skip the filter entirely. */\n static async load(\n root: string,\n includeHidden: boolean,\n signal?: AbortSignal,\n maxDepth?: number,\n ignoreCeiling?: string,\n ): Promise<GitignoreManager | null> {\n const manager = new GitignoreManager();\n try {\n const gitignorePaths: string[] = [];\n // Search dot-directories (.venv, .terraform, ...) for rules only when the\n // walk includes hidden entries; for any other walk, enumerating every\n // hidden tree would cost more than the walk itself.\n const gitignorePatterns = includeHidden\n ? [...buildHiddenPatterns('**/.gitignore')]\n : ['**/.gitignore'];\n const gitignoreEntries = fsGlob(gitignorePatterns, {\n cwd: root,\n // Skip what the walk itself skips, and stop one level past its depth\n // bound: a .gitignore at depth d only affects entries at depth >= d.\n exclude: (entry: string) => {\n if (signal?.aborted) return true;\n const name = basename(entry);\n if (name === '.hg' || name === '.svn' || DEFAULT_EXCLUDED_NAMES.has(name)) return true;\n return maxDepth !== undefined && toPosixPath(entry).split('/').length - 1 > maxDepth;\n },\n });\n for await (const match of gitignoreEntries) {\n if (signal?.aborted) break;\n gitignorePaths.push(match);\n }\n\n await loadGitignoreFiles(root, gitignorePaths, manager, signal);\n if (ignoreCeiling !== undefined) {\n await loadAncestorGitignores(root, ignoreCeiling, manager, signal);\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n Logger.warn(\n `Failed to enumerate .gitignore files under ${root}: ${formatUnknownErrorMessage(error)}`,\n );\n }\n return manager.matchers.size === 0 && manager.ancestors.length === 0 ? null : manager;\n }\n\n isIgnored(relativePath: string, isDirectory: boolean): boolean {\n const normalized = toPosixPath(relativePath);\n if (normalized === '' || normalized === '.') return false;\n const parts = normalized.split('/');\n\n // 1. Check parent directories first\n let currentDir = '';\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!part) continue;\n currentDir = currentDir ? `${currentDir}/${part}` : part;\n\n if (this.checkPath(currentDir, true)) {\n return true;\n }\n }\n\n // 2. Check the file/directory itself\n return this.checkPath(normalized, isDirectory);\n }\n\n private checkPath(posixPath: string, isDirectory: boolean): boolean {\n const parts = posixPath.split('/');\n const pathToCheck = isDirectory\n ? posixPath.endsWith('/')\n ? posixPath\n : `${posixPath}/`\n : posixPath;\n\n let ignored = false;\n\n for (const { prefix, matcher } of this.ancestors) {\n const res = matcher.test(`${prefix}/${pathToCheck}`);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n\n // Check root level\n const rootMatcher = this.matchers.get('');\n if (rootMatcher) {\n const res = rootMatcher.test(pathToCheck);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n\n // Check subdirectories\n let currentDir = '';\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!part) continue;\n currentDir = currentDir ? `${currentDir}/${part}` : part;\n\n const matcher = this.matchers.get(currentDir);\n if (matcher) {\n const relParts = parts.slice(i + 1);\n const relPath = relParts.join('/');\n const relPathToCheck = isDirectory\n ? relPath.endsWith('/')\n ? relPath\n : `${relPath}/`\n : relPath;\n\n const res = matcher.test(relPathToCheck);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n }\n\n return ignored;\n }\n}\n\ninterface GlobDirentLike extends DirentLike {\n name: string;\n parentPath: string;\n}\n\nexport interface GlobEntry {\n path: string;\n dirent: DirentLike;\n}\n\nexport interface GlobEntriesOptions {\n cwd: string;\n pattern: string;\n includeHidden?: boolean;\n baseNameMatch?: boolean;\n maxDepth?: number;\n onlyFiles?: boolean;\n suppressErrors?: boolean;\n /** Skip what a walk should not surface: DEFAULT_EXCLUDED_NAMES and .gitignore. */\n skipIgnored?: boolean;\n /**\n * Topmost directory whose `.gitignore` may apply to this walk — the allowed\n * root containing `cwd`. Ancestors of `cwd` up to and including it are\n * consulted; nothing above it is ever read. Ignored without `skipIgnored`.\n */\n ignoreCeiling?: string;\n /** Bounds the `.gitignore` discovery `skipIgnored` runs before the walk. */\n signal?: AbortSignal;\n}\n\ninterface NormalizedGlob {\n cwd: string;\n patterns: readonly string[];\n suppressErrors: boolean;\n maxDepth?: number;\n}\n\nconst GLOB_MAGIC_RE = /[*?[\\]{}!]/u;\nconst GLOB_BATCH_CONCURRENCY = 64;\nconst SEP = '/';\nfunction normalizePattern(pattern: string, baseNameMatch: boolean): string {\n const normalized = toPosixPath(pattern);\n\n if (!baseNameMatch) return normalized;\n if (normalized.includes(SEP)) return normalized;\n return `**/${normalized}`;\n}\n\nfunction splitPatternPrefix(normalizedPattern: string): {\n prefix: string;\n remainder: string;\n} {\n if (!GLOB_MAGIC_RE.test(normalizedPattern)) {\n return { prefix: '', remainder: normalizedPattern };\n }\n\n const segments = normalizedPattern.split(SEP);\n const splitIndex = segments.findIndex((seg) => GLOB_MAGIC_RE.test(seg));\n\n if (splitIndex <= 0) {\n return { prefix: '', remainder: normalizedPattern };\n }\n\n return {\n prefix: segments.slice(0, splitIndex).join(SEP) + SEP,\n remainder: segments.slice(splitIndex).join(SEP),\n };\n}\n\n/**\n * The segment matching the dot-prefixed names `segment` would match if fs.glob\n * did not skip them, or `undefined` when there are none. Only a leading `*` or\n * `?` stands for that dot: a literal never does, and a character class or a\n * brace set is left as written rather than parsed.\n */\nfunction dotVariant(segment: string): string | undefined {\n // `*(…)` and `?(…)` are extglobs, not wildcards.\n if (segment[1] === '(') return undefined;\n if (segment.startsWith('*')) return `.${segment}`;\n if (!segment.startsWith('?')) return undefined;\n // `?` is exactly one character, so the dot replaces it, unless what follows\n // could leave just `.` or `..` (`?`, `?.`, `?{.,x}`, `?[.]`): the directory\n // and its parent, not a dot-prefixed entry.\n const rest = segment.slice(1);\n return rest === '' || /^[.[{]/u.test(rest) ? undefined : `.${rest}`;\n}\n\nfunction addFirstDotSegment(patterns: Set<string>, prefix: string, remainder: string): void {\n if (remainder.length === 0) return;\n const segments = remainder.split(SEP);\n const idx = segments.findIndex((seg) => seg !== '**' && seg.length > 0);\n\n if (idx !== -1) {\n const original = segments[idx];\n const dotted = original ? dotVariant(original) : undefined;\n if (dotted) {\n const newSegments = [...segments];\n newSegments[idx] = dotted;\n patterns.add(`${prefix}${newSegments.join(SEP)}`);\n }\n }\n}\n\nfunction expandHiddenGlobstars(patterns: Set<string>, prefix: string, remainder: string): void {\n // Trailing bare globstar — `src/**`, `**` (remainder is exactly `**`, no\n // following segment). The pattern itself already matches every non-dot\n // entry under the prefix; add the two hidden complements fs.glob skips:\n // dotfiles/dot-dirs anywhere, and the contents of dot-dirs anywhere.\n if (remainder === '**') {\n patterns.add(`${prefix}**/.*`);\n patterns.add(`${prefix}**/.*/**`);\n return;\n }\n\n if (!remainder.startsWith('**/')) return;\n\n const afterGlobstar = remainder.slice(3);\n // Node fs.glob skips leading-dot entries unless the matching segment itself\n // starts with '.', and its `dot` option is not honored. So `**/*` alone misses\n // hidden files. Two depth-agnostic patterns cover everything the per-depth\n // unroll did: contents of dot-directories anywhere, and dotfiles/dotdirs\n // anywhere (when the first segment after the globstar can match a dot).\n patterns.add(`${prefix}**/.*/**/${afterGlobstar}`);\n const [firstSegment = '', ...rest] = afterGlobstar.split(SEP);\n const dotted = dotVariant(firstSegment);\n if (dotted) patterns.add(`${prefix}**/${[dotted, ...rest].join(SEP)}`);\n}\n\nfunction buildHiddenPatterns(normalizedPattern: string): readonly string[] {\n const patterns = new Set<string>([normalizedPattern]);\n const { prefix, remainder } = splitPatternPrefix(normalizedPattern);\n\n addFirstDotSegment(patterns, prefix, remainder);\n expandHiddenGlobstars(patterns, prefix, remainder);\n\n return Array.from(patterns);\n}\n\nfunction normalizeGlobOptions(options: GlobEntriesOptions): NormalizedGlob {\n const cwd = resolve(options.cwd);\n const normalizedPattern = normalizePattern(options.pattern, options.baseNameMatch ?? false);\n\n const patterns = options.includeHidden\n ? buildHiddenPatterns(normalizedPattern)\n : [normalizedPattern];\n\n const normalized: NormalizedGlob = {\n cwd,\n patterns,\n suppressErrors: options.suppressErrors ?? false,\n };\n\n if (options.maxDepth !== undefined) {\n normalized.maxDepth = options.maxDepth;\n }\n\n return normalized;\n}\n\nfunction* processDirentMatch(\n match: GlobDirentLike,\n cwd: string,\n maxDepth: number | undefined,\n seen: Set<string>,\n onlyFiles: boolean,\n): Generator<GlobEntry> {\n const absolutePath = resolve(cwd, match.parentPath, match.name);\n // The walk's scope is its cwd, whatever the pattern spells: a `..` or a root\n // the validator did not foresee must not reach a caller, where it would be\n // read, rewritten, or counted and named as inaccessible.\n if (!isPathInsideDirectory(cwd, absolutePath)) return;\n\n if (maxDepth !== undefined) {\n const rel = relative(cwd, absolutePath);\n if (rel.split(/[/\\\\]/u).length - 1 > maxDepth) return;\n }\n\n if (seen.has(absolutePath)) return;\n seen.add(absolutePath);\n\n if (onlyFiles && !match.isFile()) return;\n yield { path: absolutePath, dirent: match };\n}\n\ninterface WalkFilter {\n /** Given to fs.glob: true stops descent into a directory. */\n prune: (match: GlobDirentLike) => boolean;\n /** Re-applied to yielded entries: the ignore rules only, never the depth bound. */\n isExcluded: (match: GlobDirentLike) => boolean;\n}\n\nfunction createWalkFilter(\n cwd: string,\n skipIgnored: boolean,\n gitignoreMatcher: GitignoreManager | null,\n maxDepth: number | undefined,\n signal: AbortSignal | undefined,\n): WalkFilter {\n // fs.glob can hand the predicate a dirent whose parentPath is \".\" — the walk\n // root, not the process cwd — when it re-visits a directory under `**` whose\n // name also exists in the process cwd. Resolving against `cwd` names the\n // entry the dirent describes; `join` would resolve it against the process.\n const absoluteOf = (match: GlobDirentLike): string => resolve(cwd, match.parentPath, match.name);\n const relativeOf = (match: GlobDirentLike): string =>\n toPosixPath(relative(cwd, absoluteOf(match)));\n // Every default exclude is a bare name that applies at any depth, so a Set\n // lookup per segment replaces compiling dozens of globs per entry.\n const excluded = (posixRel: string, isDir: boolean): boolean =>\n skipIgnored &&\n (posixRel.split('/').some((segment) => DEFAULT_EXCLUDED_NAMES.has(segment)) ||\n (gitignoreMatcher?.isIgnored(posixRel, isDir) ?? false));\n\n return {\n prune: (match) => {\n // fs.glob takes no signal and calls this for every entry it visits, so\n // pruning everything is the only way to stop a walk that yields nothing.\n if (signal?.aborted) return true;\n // fs.glob asks before it queues a directory, so a walk that would\n // climb to `../` or start at `/` stops at the edge instead of reading\n // there. Not every climb asks (`**/..` does not): processDirentMatch is\n // the gate, this only saves the walk.\n if (!isPathInsideDirectory(cwd, absoluteOf(match))) return true;\n const posixRel = relativeOf(match);\n const isDir = match.isDirectory();\n if (excluded(posixRel, isDir)) return true;\n // Stop descent one level past the depth bound. Pruning a directory *at*\n // the bound would drop it where fs.glob treats it as top-level, so prune\n // its children instead; processDirentMatch drops that extra level.\n if (maxDepth === undefined || !isDir) return false;\n return posixRel.split('/').length - 1 > maxDepth;\n },\n isExcluded: (match) => excluded(relativeOf(match), match.isDirectory()),\n };\n}\n\nasync function* processGlobPattern(\n pattern: string,\n plan: NormalizedGlob,\n seen: Set<string>,\n onlyFiles: boolean,\n filter: WalkFilter,\n signal: AbortSignal | undefined,\n): AsyncGenerator<GlobEntry> {\n const { cwd, maxDepth, suppressErrors } = plan;\n let iterable: AsyncIterable<GlobDirentLike>;\n try {\n iterable = fsGlob(pattern, {\n cwd,\n exclude: filter.prune,\n withFileTypes: true,\n }) as AsyncIterable<GlobDirentLike>;\n } catch (error) {\n if (suppressErrors) return;\n throw error;\n }\n\n try {\n for await (const match of iterable) {\n if (signal?.aborted) break;\n // A function `exclude` only prunes descent in fs.glob — it still yields\n // the rejected dirent itself, and any rejected entry below the top level.\n // Re-apply the ignore rules (not the depth bound) to drop those.\n if (filter.isExcluded(match)) continue;\n yield* processDirentMatch(match, cwd, maxDepth, seen, onlyFiles);\n }\n } catch (error) {\n if (!suppressErrors) throw error;\n Logger.warn(\n `globEntries: suppressed mid-walk error for pattern \"${pattern}\": ${formatUnknownErrorMessage(error)}`,\n );\n }\n // A pruned walk ends like a finished one. Say it was cut short, so no caller\n // reads \"the walk ended\" as \"the whole tree was seen\".\n signal?.throwIfAborted();\n}\n\nexport async function* globEntries(options: GlobEntriesOptions): AsyncGenerator<GlobEntry> {\n const gitignoreMatcher = options.skipIgnored\n ? await GitignoreManager.load(\n options.cwd,\n options.includeHidden ?? false,\n options.signal,\n options.maxDepth,\n options.ignoreCeiling,\n )\n : null;\n\n const plan = normalizeGlobOptions(options);\n const seen = new Set<string>();\n const onlyFiles = options.onlyFiles ?? true;\n const filter = createWalkFilter(\n plan.cwd,\n options.skipIgnored ?? false,\n gitignoreMatcher,\n plan.maxDepth,\n options.signal,\n );\n\n for (const pattern of plan.patterns) {\n yield* processGlobPattern(pattern, plan, seen, onlyFiles, filter, options.signal);\n }\n}\n\n/** Names a `skipIgnored` walk never enters or yields, at any depth. */\nconst DEFAULT_EXCLUDED_NAMES: ReadonlySet<string> = new Set([\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n '.git',\n '.vscode',\n '.idea',\n '.DS_Store',\n '.next',\n '.nuxt',\n '.output',\n '.svelte-kit',\n '.cache',\n '.yarn',\n 'jspm_packages',\n 'bower_components',\n 'out',\n 'tmp',\n '.temp',\n 'npm-debug.log',\n 'yarn-debug.log',\n 'yarn-error.log',\n 'Thumbs.db',\n]);\n"]}
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../../src/core/glob.ts"],"names":[],"mappings":"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","sourcesContent":["import { glob as fsGlob, readFile as fsReadFile, lstat } from 'node:fs/promises';\nimport { basename, dirname, join, matchesGlob, relative, resolve, win32 } from 'node:path';\n\nimport type { Ignore } from 'ignore';\nimport ignore from 'ignore';\n\nimport { processInParallel } from './concurrency.ts';\nimport { formatUnknownErrorMessage } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport type { DirentLike } from './path-utils.ts';\nimport { IS_WINDOWS, isPathInsideDirectory, toPosixPath } from './path-utils.ts';\nimport { MIB } from './util.ts';\n\n/**\n * More brace alternatives than this is not a file filter. fs.glob's matcher\n * dedupes its alternatives pairwise, synchronously: `{1..100000}` pins the\n * event loop for most of a minute, where 1024 costs tens of milliseconds.\n */\nexport const MAX_BRACE_ALTERNATIVES = 1024;\n\n/** Bound synchronous glob compilation when checking candidate traversal segments. */\nexport const MAX_DOTDOT_CHECK_CHARS = 4096;\n\n/**\n * fs.glob tests each name against a regex compiled from the segment,\n * synchronously. Every run of `*` and every extglob group (`?(`, `+(`, `@(`,\n * `!(`, `*(`) adds a degree of backtracking: on a 255-character name three cost\n * under 100 ms, four about 1 s, and five do not finish; a chain of `?(a|aa)`\n * groups grows the same way.\n */\nexport const MAX_SEGMENT_WILDCARDS = 3;\n\n// The range bodies fs.glob's brace expansion recognises, as it writes them.\nconst NUMERIC_RANGE_RE = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/u;\nconst ALPHA_RANGE_RE = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/u;\n\n/** The values a range body (`x..y`, `x..y..step`) expands to, or `undefined` past the cap. */\nfunction rangeValues(body: string): string[] | undefined {\n const alpha = ALPHA_RANGE_RE.test(body);\n const [from = '', to = '', step = '1'] = body.split('..');\n const bound = (end: string): number => (alpha ? end.charCodeAt(0) : Number.parseInt(end, 10));\n const start = bound(from);\n const stop = bound(to);\n const stride = Math.max(Math.abs(Number.parseInt(step, 10)), 1);\n // Past 2^53 a step no longer moves the number (2^53 + 1 === 2^53), and past\n // Number.MAX_VALUE it reads as Infinity: either way the loop below would\n // never end. Refuse them like any other long range.\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(stop)) return undefined;\n if (Math.floor(Math.abs(stop - start) / stride) >= MAX_BRACE_ALTERNATIVES) return undefined;\n const values: string[] = [];\n const direction = stop < start ? -1 : 1;\n for (let i = start; (stop - i) * direction >= 0; i += stride * direction) {\n // The engine drops a `\\` a letter range passes through (`{Z..a}`), and\n // digits can never spell a root or a `..`, so their padding is moot.\n const char = String.fromCharCode(i);\n values.push(alpha ? (char === '\\\\' ? '' : char) : String(i));\n }\n return values;\n}\n\ninterface BraceSet {\n pre: string;\n /** `undefined` when the set alone expands past the cap. */\n alternatives: string[] | undefined;\n post: string;\n}\n\n/**\n * The leftmost `{…}` that fs.glob would expand: a comma list or a range.\n * The expansion is textual, as fs.glob's is: a brace inside `[…]` still\n * counts (unlike SensitiveMatcher's own glob dialect in sensitive.ts).\n */\nfunction firstBraceSet(pattern: string): BraceSet | undefined {\n const open: { at: number; commas: number[] }[] = [];\n let first: { at: number; commas: number[]; close: number } | undefined;\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === '{') open.push({ at: i, commas: [] });\n else if (char === ',') open.at(-1)?.commas.push(i);\n else if (char === '}') {\n const set = open.pop();\n if (!set || (first && first.at < set.at)) continue;\n const body = pattern.slice(set.at + 1, i);\n if (set.commas.length > 0 || NUMERIC_RANGE_RE.test(body) || ALPHA_RANGE_RE.test(body)) {\n first = { ...set, close: i };\n }\n }\n }\n if (!first) return undefined;\n let alternatives: string[] | undefined;\n if (first.commas.length > 0) {\n alternatives = [];\n let start = first.at + 1;\n for (const end of [...first.commas, first.close]) {\n alternatives.push(pattern.slice(start, end));\n start = end + 1;\n }\n } else {\n alternatives = rangeValues(pattern.slice(first.at + 1, first.close));\n }\n return { pre: pattern.slice(0, first.at), alternatives, post: pattern.slice(first.close + 1) };\n}\n\n/** Every brace alternative of `pattern`, or `undefined` past the cap. */\nfunction expandBraceSets(pattern: string, out: string[] = []): string[] | undefined {\n const set = firstBraceSet(pattern);\n if (!set) {\n out.push(pattern);\n return out.length > MAX_BRACE_ALTERNATIVES ? undefined : out;\n }\n if (!set.alternatives) return undefined;\n for (const alternative of set.alternatives) {\n if (!expandBraceSets(`${set.pre}${alternative}${set.post}`, out)) return undefined;\n }\n return out;\n}\n\n/** The characters minimatch can spell a literal `..` with: dots and one-character classes. */\nconst DOTDOT_SPELLING_RE = /^[.[\\]-]+$/u;\n\n/**\n * Whether the glob engine reads this brace-free segment as `..`. readdir never\n * yields `..`, so only a literal reaches it, and minimatch spells a literal dot\n * as `.`, `[.]` or `[.-.]`. Any other character (`*`, `?`, an extglob, a\n * letter) either makes the segment a pattern the walk never follows to `..`,\n * or puts that character in the name. Only the rest is worth asking the engine\n * about, so a long or exotic segment costs no compile; one the engine cannot\n * compile is refused.\n */\nconst isTraversalSegment = (segment: string): boolean => {\n try {\n return matchesGlob('..', segment);\n } catch {\n return true;\n }\n};\n\n/**\n * An alternative that starts at a root: `/x`, `//host/share`, `C:/x`, `C:x`,\n * on any host. On Windows fs.glob also reads a literal first segment ending in\n * `:` as a drive (`[C]:/x`); elsewhere that is an ordinary name (`logs:`).\n */\nconst isRooted = (alternative: string): boolean =>\n win32.parse(alternative).root !== '' ||\n (IS_WINDOWS && (alternative.split('/')[0] ?? '').endsWith(':'));\n\n/**\n * Index of the `)` that closes the group opened at `open`, or -1. A `[...]`\n * class is skipped whole: a `)` or `|` inside one is a literal, not syntax.\n */\nfunction groupClose(segment: string, open: number): number {\n let depth = 0;\n for (let i = open; i < segment.length; i++) {\n const c = segment[i];\n if (c === '[') {\n // A `]` first in the class (or after `!`/`^`) is literal, so search past it.\n const first = segment[i + 1] === '!' || segment[i + 1] === '^' ? i + 2 : i + 1;\n const end = segment.indexOf(']', first + 1);\n if (end !== -1) {\n i = end;\n continue;\n }\n }\n if (c === '(') depth++;\n else if (c === ')' && --depth === 0) return i;\n }\n return -1;\n}\n\n/**\n * Whether matching this brace-free segment can backtrack for seconds on one\n * long name. Two shapes do: a repeated group (`*(…)`, `+(…)`) whose body can\n * match more than one way (`|`, `*`, or a nested group), and more than\n * MAX_SEGMENT_WILDCARDS `*` runs and extglob groups together.\n */\nfunction canBacktrack(segment: string): boolean {\n if (segment === '**') return false;\n let wildcards = (segment.match(/\\*+/gu) ?? []).length;\n for (let i = segment.indexOf('('); i !== -1; i = segment.indexOf('(', i + 1)) {\n const opener = segment[i - 1];\n if (opener === undefined || !'*+?@!'.includes(opener)) continue;\n // Each group can match more than one way, so it adds a degree of\n // backtracking like a `*` run does (`*(` is already counted as one).\n if (opener !== '*') wildcards++;\n if (opener !== '*' && opener !== '+') continue;\n const close = groupClose(segment, i);\n // Classes are literals: only `|`, `*` or a group outside one can backtrack.\n const body = segment\n .slice(i + 1, close === -1 ? undefined : close)\n .replace(/\\[[!^]?\\]?[^\\]]*\\]/gu, '');\n if (/[|*(]/u.test(body)) return true;\n }\n return wildcards > MAX_SEGMENT_WILDCARDS;\n}\n\nexport function isSafeGlobSyntax(pattern: string): boolean {\n if (!pattern || pattern.trim().length === 0) {\n return false;\n }\n // The walk sees the pattern with `\\` as `/` (normalizePattern), so check\n // that form: `\\etc\\x` is `/etc/x` there, an absolute walk on any host.\n // Brace sets can build a root or a `..` from parts (`{/etc,x}`, `..{,x}`,\n // `.{Z..a}.`), so check every alternative, not the pattern as written.\n // globEntries still drops any match outside its cwd: this check is what\n // turns such a pattern into an error rather than a quietly narrower walk.\n const alternatives = expandBraceSets(toPosixPath(pattern));\n if (!alternatives) return false;\n const segments = new Set<string>();\n for (const alternative of alternatives) {\n if (isRooted(alternative)) return false;\n for (const segment of alternative.split('/')) segments.add(segment);\n }\n let dotdotCheckChars = 0;\n for (const segment of segments) {\n if (canBacktrack(segment)) return false;\n if (!segment.includes('.') || !DOTDOT_SPELLING_RE.test(segment)) continue;\n dotdotCheckChars += segment.length;\n if (dotdotCheckChars > MAX_DOTDOT_CHECK_CHARS || isTraversalSegment(segment)) return false;\n }\n return true;\n}\n\n/** A .gitignore past this size is not a .gitignore; skip it rather than read it. */\nconst MAX_GITIGNORE_BYTES = MIB;\n\nasync function loadGitignoreFiles(\n root: string,\n gitignorePaths: readonly string[],\n manager: GitignoreManager,\n signal?: AbortSignal,\n): Promise<void> {\n await processInParallel(\n gitignorePaths,\n async (relPath) => {\n const absPath = join(root, relPath);\n try {\n // lstat, not stat: a symlinked .gitignore may point at a device, a\n // FIFO or a huge file outside the root. Only a plain, small file is a\n // rule set worth reading.\n const info = await lstat(absPath);\n if (!info.isFile() || info.size > MAX_GITIGNORE_BYTES) return;\n const contents = await fsReadFile(absPath, { encoding: 'utf-8', signal });\n const matcher = ignore();\n matcher.add(contents);\n const dir = toPosixPath(dirname(relPath));\n manager.addMatcher(dir === '.' ? '' : dir, matcher);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n Logger.warn(`Failed to read .gitignore at ${absPath}: ${formatUnknownErrorMessage(error)}`);\n }\n },\n GLOB_BATCH_CONCURRENCY,\n signal,\n );\n}\n\n/**\n * Load `.gitignore` from each ancestor of `root` up to and including `ceiling`,\n * farthest first. A rule set that ignores `root` itself is skipped: the caller\n * named that directory on purpose, and applying the rule would empty the walk.\n */\nasync function loadAncestorGitignores(\n root: string,\n ceiling: string,\n manager: GitignoreManager,\n signal?: AbortSignal,\n): Promise<void> {\n const normalizedCeiling = resolve(ceiling);\n const chain: string[] = [];\n for (let dir = dirname(resolve(root)); ; dir = dirname(dir)) {\n if (!isPathInsideDirectory(normalizedCeiling, dir)) break;\n chain.push(dir);\n if (\n (isPathInsideDirectory(dir, normalizedCeiling) &&\n isPathInsideDirectory(normalizedCeiling, dir)) ||\n dirname(dir) === dir\n ) {\n break;\n }\n }\n for (const dir of chain.reverse()) {\n signal?.throwIfAborted();\n const file = join(dir, '.gitignore');\n try {\n const info = await lstat(file);\n if (!info.isFile() || info.size > MAX_GITIGNORE_BYTES) continue;\n const matcher = ignore().add(await fsReadFile(file, { encoding: 'utf-8', signal }));\n const prefix = toPosixPath(relative(dir, root));\n if (matcher.test(`${prefix}/`).ignored) continue;\n manager.addAncestorMatcher(prefix, matcher);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n // ENOENT is the normal case (no .gitignore at this level); anything else is not worth a warning per walk\n }\n }\n}\n\nclass GitignoreManager {\n private matchers = new Map<string, Ignore>();\n /** Rules from directories above the walk root, farthest first; `prefix` re-bases a walk-relative path to that directory. */\n private ancestors: { prefix: string; matcher: Ignore }[] = [];\n private dirCache = new Map<string, boolean>();\n\n addMatcher(dir: string, matcher: Ignore): void {\n this.matchers.set(dir, matcher);\n }\n\n addAncestorMatcher(prefix: string, matcher: Ignore): void {\n this.ancestors.push({ prefix, matcher });\n }\n\n /** `null` when no `.gitignore` was found, so callers skip the filter entirely. */\n static async load(\n root: string,\n includeHidden: boolean,\n signal?: AbortSignal,\n maxDepth?: number,\n ignoreCeiling?: string,\n ): Promise<GitignoreManager | null> {\n const manager = new GitignoreManager();\n try {\n const gitignorePaths: string[] = [];\n // Search dot-directories (.venv, .terraform, ...) for rules only when the\n // walk includes hidden entries; for any other walk, enumerating every\n // hidden tree would cost more than the walk itself.\n const gitignorePatterns = includeHidden\n ? [...buildHiddenPatterns('**/.gitignore')]\n : ['**/.gitignore'];\n const gitignoreEntries = fsGlob(gitignorePatterns, {\n cwd: root,\n // Skip what the walk itself skips, and stop one level past its depth\n // bound: a .gitignore at depth d only affects entries at depth >= d.\n exclude: (entry: string) => {\n if (signal?.aborted) return true;\n const name = basename(entry);\n if (name === '.hg' || name === '.svn' || DEFAULT_EXCLUDED_NAMES.has(name)) return true;\n return maxDepth !== undefined && toPosixPath(entry).split('/').length - 1 > maxDepth;\n },\n });\n for await (const match of gitignoreEntries) {\n if (signal?.aborted) break;\n gitignorePaths.push(match);\n }\n\n await loadGitignoreFiles(root, gitignorePaths, manager, signal);\n if (ignoreCeiling !== undefined) {\n await loadAncestorGitignores(root, ignoreCeiling, manager, signal);\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') throw error;\n Logger.warn(\n `Failed to enumerate .gitignore files under ${root}: ${formatUnknownErrorMessage(error)}`,\n );\n }\n return manager.matchers.size === 0 && manager.ancestors.length === 0 ? null : manager;\n }\n\n isIgnored(relativePath: string, isDirectory: boolean): boolean {\n const normalized = toPosixPath(relativePath);\n if (normalized === '' || normalized === '.') return false;\n const parts = normalized.split('/');\n\n // 1. Check parent directories first\n let currentDir = '';\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!part) continue;\n currentDir = currentDir ? `${currentDir}/${part}` : part;\n\n if (this.checkDir(currentDir)) {\n return true;\n }\n }\n\n // 2. Check the file/directory itself\n return isDirectory ? this.checkDir(normalized) : this.checkPath(normalized, false);\n }\n\n private checkDir(dir: string): boolean {\n const cached = this.dirCache.get(dir);\n if (cached !== undefined) return cached;\n const res = this.checkPath(dir, true);\n this.dirCache.set(dir, res);\n return res;\n }\n\n private checkPath(posixPath: string, isDirectory: boolean): boolean {\n const parts = posixPath.split('/');\n const pathToCheck = isDirectory\n ? posixPath.endsWith('/')\n ? posixPath\n : `${posixPath}/`\n : posixPath;\n\n let ignored = false;\n\n for (const { prefix, matcher } of this.ancestors) {\n const res = matcher.test(`${prefix}/${pathToCheck}`);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n\n // Check root level\n const rootMatcher = this.matchers.get('');\n if (rootMatcher) {\n const res = rootMatcher.test(pathToCheck);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n\n // Check subdirectories\n let currentDir = '';\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!part) continue;\n currentDir = currentDir ? `${currentDir}/${part}` : part;\n\n const matcher = this.matchers.get(currentDir);\n if (matcher) {\n const relParts = parts.slice(i + 1);\n const relPath = relParts.join('/');\n const relPathToCheck = isDirectory\n ? relPath.endsWith('/')\n ? relPath\n : `${relPath}/`\n : relPath;\n\n const res = matcher.test(relPathToCheck);\n if (res.ignored) ignored = true;\n if (res.unignored) ignored = false;\n }\n }\n\n return ignored;\n }\n}\n\ninterface GlobDirentLike extends DirentLike {\n name: string;\n parentPath: string;\n}\n\nexport interface GlobEntry {\n path: string;\n dirent: DirentLike;\n}\n\nexport interface GlobEntriesOptions {\n cwd: string;\n pattern: string;\n includeHidden?: boolean;\n baseNameMatch?: boolean;\n maxDepth?: number;\n onlyFiles?: boolean;\n suppressErrors?: boolean;\n /** Skip what a walk should not surface: DEFAULT_EXCLUDED_NAMES and .gitignore. */\n skipIgnored?: boolean;\n /**\n * Topmost directory whose `.gitignore` may apply to this walk — the allowed\n * root containing `cwd`. Ancestors of `cwd` up to and including it are\n * consulted; nothing above it is ever read. Ignored without `skipIgnored`.\n */\n ignoreCeiling?: string;\n /** Bounds the `.gitignore` discovery `skipIgnored` runs before the walk. */\n signal?: AbortSignal;\n}\n\ninterface NormalizedGlob {\n cwd: string;\n patterns: readonly string[];\n suppressErrors: boolean;\n maxDepth?: number;\n}\n\nconst GLOB_MAGIC_RE = /[*?[\\]{}!]/u;\nconst GLOB_BATCH_CONCURRENCY = 64;\nconst SEP = '/';\nfunction normalizePattern(pattern: string, baseNameMatch: boolean): string {\n const normalized = toPosixPath(pattern);\n\n if (!baseNameMatch) return normalized;\n if (normalized.includes(SEP)) return normalized;\n return `**/${normalized}`;\n}\n\nfunction splitPatternPrefix(normalizedPattern: string): {\n prefix: string;\n remainder: string;\n} {\n if (!GLOB_MAGIC_RE.test(normalizedPattern)) {\n return { prefix: '', remainder: normalizedPattern };\n }\n\n const segments = normalizedPattern.split(SEP);\n const splitIndex = segments.findIndex((seg) => GLOB_MAGIC_RE.test(seg));\n\n if (splitIndex <= 0) {\n return { prefix: '', remainder: normalizedPattern };\n }\n\n return {\n prefix: segments.slice(0, splitIndex).join(SEP) + SEP,\n remainder: segments.slice(splitIndex).join(SEP),\n };\n}\n\n/**\n * The segment matching the dot-prefixed names `segment` would match if fs.glob\n * did not skip them, or `undefined` when there are none. Only a leading `*` or `?`\n * stands for that dot: a literal never does, and a character class or a\n * brace set is left as written rather than parsed.\n */\nfunction dotVariant(segment: string): string | undefined {\n // `*(…)`, `?(…)` and `!(…)` are extglobs, not wildcards.\n if (segment[1] === '(') return undefined;\n if (segment.startsWith('*')) return `.${segment}`;\n if (!segment.startsWith('?')) return undefined;\n // `?` is exactly one character, so the dot replaces it, unless what follows\n // could leave just `.` or `..` (`?`, `?.`, `?{.,x}`, `?[.]`): the directory\n // and its parent, not a dot-prefixed entry.\n const rest = segment.slice(1);\n return rest === '' || /^[.[{]/u.test(rest) ? undefined : `.${rest}`;\n}\n\nfunction addFirstDotSegment(patterns: Set<string>, prefix: string, remainder: string): void {\n if (remainder.length === 0) return;\n const segments = remainder.split(SEP);\n const idx = segments.findIndex((seg) => seg !== '**' && seg.length > 0);\n\n if (idx !== -1) {\n const original = segments[idx];\n const dotted = original ? dotVariant(original) : undefined;\n if (dotted) {\n const newSegments = [...segments];\n newSegments[idx] = dotted;\n patterns.add(`${prefix}${newSegments.join(SEP)}`);\n }\n }\n}\n\nfunction expandHiddenGlobstars(patterns: Set<string>, prefix: string, remainder: string): void {\n // Trailing bare globstar — `src/**`, `**` (remainder is exactly `**`, no\n // following segment). The pattern itself already matches every non-dot\n // entry under the prefix; add the two hidden complements fs.glob skips:\n // dotfiles/dot-dirs anywhere, and the contents of dot-dirs anywhere.\n if (remainder === '**') {\n patterns.add(`${prefix}**/.*`);\n patterns.add(`${prefix}**/.*/**`);\n return;\n }\n\n if (!remainder.startsWith('**/')) return;\n\n const afterGlobstar = remainder.slice(3);\n // Node fs.glob skips leading-dot entries unless the matching segment itself\n // starts with '.', and its `dot` option is not honored. So `**/*` alone misses\n // hidden files. Two depth-agnostic patterns cover everything the per-depth\n // unroll did: contents of dot-directories anywhere, and dotfiles/dotdirs\n // anywhere (when the first segment after the globstar can match a dot).\n patterns.add(`${prefix}**/.*/**/${afterGlobstar}`);\n const [firstSegment = '', ...rest] = afterGlobstar.split(SEP);\n const dotted = dotVariant(firstSegment);\n if (dotted) patterns.add(`${prefix}**/${[dotted, ...rest].join(SEP)}`);\n}\n\nfunction buildHiddenPatterns(normalizedPattern: string): readonly string[] {\n const patterns = new Set<string>([normalizedPattern]);\n const { prefix, remainder } = splitPatternPrefix(normalizedPattern);\n\n addFirstDotSegment(patterns, prefix, remainder);\n expandHiddenGlobstars(patterns, prefix, remainder);\n\n return Array.from(patterns);\n}\n\nfunction normalizeGlobOptions(options: GlobEntriesOptions): NormalizedGlob {\n const cwd = resolve(options.cwd);\n const normalizedPattern = normalizePattern(options.pattern, options.baseNameMatch ?? false);\n\n const patterns = options.includeHidden\n ? buildHiddenPatterns(normalizedPattern)\n : [normalizedPattern];\n\n const normalized: NormalizedGlob = {\n cwd,\n patterns,\n suppressErrors: options.suppressErrors ?? false,\n };\n\n if (options.maxDepth !== undefined) {\n normalized.maxDepth = options.maxDepth;\n }\n\n return normalized;\n}\n\nfunction* processDirentMatch(\n match: GlobDirentLike,\n cwd: string,\n maxDepth: number | undefined,\n seen: Set<string>,\n onlyFiles: boolean,\n): Generator<GlobEntry> {\n const absolutePath = resolve(cwd, match.parentPath, match.name);\n // The walk's scope is its cwd, whatever the pattern spells: a `..` or a root\n // the validator did not foresee must not reach a caller, where it would be\n // read, rewritten, or counted and named as inaccessible.\n if (!isPathInsideDirectory(cwd, absolutePath)) return;\n\n if (maxDepth !== undefined) {\n const rel = relative(cwd, absolutePath);\n if (rel.split(/[/\\\\]/u).length - 1 > maxDepth) return;\n }\n\n if (seen.has(absolutePath)) return;\n seen.add(absolutePath);\n\n if (onlyFiles && !match.isFile()) return;\n yield { path: absolutePath, dirent: match };\n}\n\ninterface WalkFilter {\n /** Given to fs.glob: true stops descent into a directory. */\n prune: (match: GlobDirentLike) => boolean;\n /** Re-applied to yielded entries: the ignore rules only, never the depth bound. */\n isExcluded: (match: GlobDirentLike) => boolean;\n}\n\nfunction createWalkFilter(\n cwd: string,\n skipIgnored: boolean,\n gitignoreMatcher: GitignoreManager | null,\n maxDepth: number | undefined,\n signal: AbortSignal | undefined,\n): WalkFilter {\n // fs.glob can hand the predicate a dirent whose parentPath is \".\" — the walk\n // root, not the process cwd — when it re-visits a directory under `**` whose\n // name also exists in the process cwd. Resolving against `cwd` names the\n // entry the dirent describes; `join` would resolve it against the process.\n const absoluteOf = (match: GlobDirentLike): string => resolve(cwd, match.parentPath, match.name);\n const relativeOf = (match: GlobDirentLike): string =>\n toPosixPath(relative(cwd, absoluteOf(match)));\n // Every default exclude is a bare name that applies at any depth, so a Set\n // lookup per segment replaces compiling dozens of globs per entry.\n const excluded = (posixRel: string, isDir: boolean): boolean =>\n skipIgnored &&\n (posixRel.split('/').some((segment) => DEFAULT_EXCLUDED_NAMES.has(segment)) ||\n (gitignoreMatcher?.isIgnored(posixRel, isDir) ?? false));\n\n return {\n prune: (match) => {\n // fs.glob takes no signal and calls this for every entry it visits, so\n // pruning everything is the only way to stop a walk that yields nothing.\n if (signal?.aborted) return true;\n // fs.glob asks before it queues a directory, so a walk that would\n // climb to `../` or start at `/` stops at the edge instead of reading\n // there. Not every climb asks (`**/..` does not): processDirentMatch is\n // the gate, this only saves the walk.\n if (!isPathInsideDirectory(cwd, absoluteOf(match))) return true;\n const posixRel = relativeOf(match);\n const isDir = match.isDirectory();\n if (excluded(posixRel, isDir)) return true;\n // Stop descent one level past the depth bound. Pruning a directory *at*\n // the bound would drop it where fs.glob treats it as top-level, so prune\n // its children instead; processDirentMatch drops that extra level.\n if (maxDepth === undefined || !isDir) return false;\n return posixRel.split('/').length - 1 > maxDepth;\n },\n isExcluded: (match) => excluded(relativeOf(match), match.isDirectory()),\n };\n}\n\nasync function* processGlobPatterns(\n patterns: readonly string[],\n plan: NormalizedGlob,\n seen: Set<string>,\n onlyFiles: boolean,\n filter: WalkFilter,\n signal: AbortSignal | undefined,\n): AsyncGenerator<GlobEntry> {\n const { cwd, maxDepth, suppressErrors } = plan;\n let iterable: AsyncIterable<GlobDirentLike>;\n try {\n iterable = fsGlob([...patterns], {\n cwd,\n exclude: filter.prune,\n withFileTypes: true,\n }) as AsyncIterable<GlobDirentLike>;\n } catch (error) {\n if (suppressErrors) return;\n throw error;\n }\n\n try {\n for await (const match of iterable) {\n if (signal?.aborted) break;\n // A function `exclude` only prunes descent in fs.glob — it still yields\n // the rejected dirent itself, and any rejected entry below the top level.\n // Re-apply the ignore rules (not the depth bound) to drop those.\n if (filter.isExcluded(match)) continue;\n yield* processDirentMatch(match, cwd, maxDepth, seen, onlyFiles);\n }\n } catch (error) {\n if (!suppressErrors) throw error;\n Logger.warn(\n `globEntries: suppressed mid-walk error for pattern \"${patterns.join(', ')}\": ${formatUnknownErrorMessage(error)}`,\n );\n }\n // A pruned walk ends like a finished one. Say it was cut short, so no caller\n // reads \"the walk ended\" as \"the whole tree was seen\".\n signal?.throwIfAborted();\n}\n\nexport async function* globEntries(options: GlobEntriesOptions): AsyncGenerator<GlobEntry> {\n const gitignoreMatcher = options.skipIgnored\n ? await GitignoreManager.load(\n options.cwd,\n options.includeHidden ?? false,\n options.signal,\n options.maxDepth,\n options.ignoreCeiling,\n )\n : null;\n\n const plan = normalizeGlobOptions(options);\n const seen = new Set<string>();\n const onlyFiles = options.onlyFiles ?? true;\n const filter = createWalkFilter(\n plan.cwd,\n options.skipIgnored ?? false,\n gitignoreMatcher,\n plan.maxDepth,\n options.signal,\n );\n\n // One walk for every variant: fs.glob reads each directory once and matches\n // all of them, where a walk per variant re-read the tree for each.\n yield* processGlobPatterns(plan.patterns, plan, seen, onlyFiles, filter, options.signal);\n}\n\n/** Names a `skipIgnored` walk never enters or yields, at any depth. */\nconst DEFAULT_EXCLUDED_NAMES: ReadonlySet<string> = new Set([\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n '.git',\n '.vscode',\n '.idea',\n '.DS_Store',\n '.next',\n '.nuxt',\n '.output',\n '.svelte-kit',\n '.cache',\n '.yarn',\n 'jspm_packages',\n 'bower_components',\n 'out',\n 'tmp',\n '.temp',\n 'npm-debug.log',\n 'yarn-debug.log',\n 'yarn-error.log',\n 'Thumbs.db',\n]);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"path-completer.d.ts","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAyJ3C;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBzF"}
1
+ {"version":3,"file":"path-completer.d.ts","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA2J3C;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBzF"}
@@ -71,8 +71,8 @@ async function findMatchesInDirectory(searchDir, prefix, pathGuard) {
71
71
  // The guard owns what a listing may enter: containment through the real
72
72
  // path, device names, the sensitive list, is-a-directory. A refusal is
73
73
  // "nothing to list", never an error, so root suggestions survive it.
74
- const listable = await pathGuard.validateExistingDirectory(searchDir).then(() => true, () => false);
75
- if (!listable)
74
+ const listable = await pathGuard.validateExistingDirectory(searchDir).then((validated) => validated, () => undefined);
75
+ if (listable === undefined)
76
76
  return matches;
77
77
  try {
78
78
  // Stream via opendir and collect every match. Do not cap here: the
@@ -81,7 +81,9 @@ async function findMatchesInDirectory(searchDir, prefix, pathGuard) {
81
81
  // and already sorted (mergeCompletionMatches). Capping here would
82
82
  // keep the opendir-first 100, not the alphabetically-first 100, and
83
83
  // hide the true count from the client.
84
- const dir = await opendir(searchDir);
84
+ // The directory the guard validated: a spelling that differs from the
85
+ // disk only in Unicode (#101) passes validation but is not on disk as typed.
86
+ const dir = await opendir(listable);
85
87
  try {
86
88
  const lowerPrefix = prefix.toLowerCase();
87
89
  for await (const entry of dir) {
@@ -1 +1 @@
1
- {"version":3,"file":"path-completer.js","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"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","sourcesContent":["import { opendir } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\n\nimport { isSkippableErrno, rethrowIfAborted } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { isSamePath, isSlash, normalizePath, toPosixPath } from './path-utils.ts';\nimport type { PathGuard } from './path.ts';\n\n// ─── pure path-completion helpers ───────────────────────────────────────────\n\nfunction hasTrailingSeparator(value: string): boolean {\n return value.length > 0 && isSlash(value.charCodeAt(value.length - 1));\n}\n\nfunction resolveFromBase(\n base: string,\n rawValue: string,\n trailingSeparator: boolean,\n): { searchDir: string; prefix: string } {\n const normalizedValue = normalizePath(resolve(base, rawValue));\n if (trailingSeparator) return { searchDir: normalizedValue, prefix: '' };\n return {\n searchDir: dirname(normalizedValue),\n prefix: basename(normalizedValue),\n };\n}\n\nfunction parseNamedRootInput(value: string): { rootName: string; remainder: string } | undefined {\n const normalizedInput = toPosixPath(value);\n if (!normalizedInput) return undefined;\n const slashIndex = normalizedInput.indexOf('/');\n if (slashIndex === -1) return { rootName: normalizedInput, remainder: '' };\n const rootName = normalizedInput.slice(0, slashIndex);\n if (!rootName) return undefined;\n return { rootName, remainder: normalizedInput.slice(slashIndex + 1) };\n}\n\nfunction findAllowedRootByName(rootName: string, allowed: readonly string[]): string | undefined {\n const normalizedRootName = rootName.toLowerCase();\n return allowed.find((candidate) => basename(candidate).toLowerCase() === normalizedRootName);\n}\n\nfunction resolveNamedRootContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const parsed = parseNamedRootInput(currentValue);\n if (!parsed) return undefined;\n const root = findAllowedRootByName(parsed.rootName, allowed);\n if (!root) return undefined;\n const trailingSeparator = hasTrailingSeparator(currentValue);\n return resolveFromBase(root, parsed.remainder, trailingSeparator);\n}\n\nfunction withDirectorySeparator(value: string): string {\n return value.endsWith(sep) ? value : `${value}${sep}`;\n}\n\nfunction collectAllowedRoots(\n allowed: readonly string[],\n predicate: (root: string) => boolean,\n): string[] {\n return allowed.filter(predicate).map(withDirectorySeparator);\n}\n\nfunction findRootPrefixMatches(currentValue: string, allowed: readonly string[]): string[] {\n const rootPrefix = parseNamedRootInput(currentValue)?.rootName.toLowerCase() ?? '';\n return collectAllowedRoots(allowed, (root) =>\n basename(root).toLowerCase().startsWith(rootPrefix),\n );\n}\n\nfunction findMatchingRoots(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n): string[] {\n const lowerPrefix = prefix.toLowerCase();\n const normalizedSearchDir = normalizePath(searchDir);\n return collectAllowedRoots(allowed, (root) => {\n const rootDir = dirname(root);\n if (!isSamePath(rootDir, normalizedSearchDir)) return false;\n return basename(root).toLowerCase().startsWith(lowerPrefix);\n });\n}\n\nfunction mergeCompletionMatches(\n dirMatches: readonly string[],\n rootMatches: readonly string[],\n): string[] {\n const isDir = (v: string): boolean => v.endsWith(sep);\n return [...new Set([...dirMatches, ...rootMatches])].sort(\n (left, right) => Number(isDir(right)) - Number(isDir(left)) || left.localeCompare(right),\n );\n}\n\nasync function findMatchesInDirectory(\n searchDir: string,\n prefix: string,\n pathGuard: PathGuard,\n): Promise<string[]> {\n const matches: string[] = [];\n // The guard owns what a listing may enter: containment through the real\n // path, device names, the sensitive list, is-a-directory. A refusal is\n // \"nothing to list\", never an error, so root suggestions survive it.\n const listable = await pathGuard.validateExistingDirectory(searchDir).then(\n () => true,\n () => false,\n );\n if (!listable) return matches;\n try {\n // Stream via opendir and collect every match. Do not cap here: the\n // SDK's completion result keeps the first 100 values and reports the\n // full length as `total` / `hasMore`, so the list must reach it whole\n // and already sorted (mergeCompletionMatches). Capping here would\n // keep the opendir-first 100, not the alphabetically-first 100, and\n // hide the true count from the client.\n const dir = await opendir(searchDir);\n try {\n const lowerPrefix = prefix.toLowerCase();\n for await (const entry of dir) {\n if (prefix !== '' && !entry.name.toLowerCase().startsWith(lowerPrefix)) continue;\n const fullPath = join(searchDir, entry.name);\n if (pathGuard.isSensitive(fullPath)) continue;\n matches.push(entry.isDirectory() ? `${fullPath}${sep}` : fullPath);\n }\n } finally {\n await dir.close().catch(() => {\n /* dir already closed or opendir never resolved */\n });\n }\n } catch (err) {\n if (!isSkippableErrno(err)) {\n Logger.warn('PathCompleter.findMatchesInDirectory: readdir failed', {\n searchDir,\n error: String(err),\n });\n }\n }\n return matches;\n}\n\nfunction getSearchContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const trailingSeparator = hasTrailingSeparator(currentValue);\n if (isAbsolute(currentValue)) {\n return resolveFromBase(parse(currentValue).root || sep, currentValue, trailingSeparator);\n }\n const namedRootContext = resolveNamedRootContext(currentValue, allowed);\n if (namedRootContext) return namedRootContext;\n if (allowed.length === 1) {\n const base = allowed[0];\n if (base) return resolveFromBase(base, currentValue, trailingSeparator);\n }\n return undefined;\n}\n\n/**\n * Path suggestions for `completion/complete` on the file template. One\n * `opendir` per call; clients debounce, so nothing is cached here. Returns\n * every match, sorted; the SDK truncates to 100 and reports the total.\n */\nexport async function suggestPaths(pathGuard: PathGuard, value: string): Promise<string[]> {\n // Roots to pick, name and list.\n const roots = pathGuard.getRoots();\n\n try {\n if (!value) {\n return roots;\n }\n\n const context = getSearchContext(value, roots);\n if (!context) {\n return findRootPrefixMatches(value, roots);\n }\n\n const { searchDir, prefix } = context;\n const dirMatches = await findMatchesInDirectory(searchDir, prefix, pathGuard);\n const rootMatches = findMatchingRoots(searchDir, prefix, roots);\n return mergeCompletionMatches(dirMatches, rootMatches);\n } catch (error) {\n rethrowIfAborted(error);\n Logger.warn('suggestPaths: completion failed, returning empty list', {\n error: error instanceof Error ? (error.stack ?? error.message) : String(error),\n });\n return [];\n }\n}\n"]}
1
+ {"version":3,"file":"path-completer.js","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"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","sourcesContent":["import { opendir } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\n\nimport { isSkippableErrno, rethrowIfAborted } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { isSamePath, isSlash, normalizePath, toPosixPath } from './path-utils.ts';\nimport type { PathGuard } from './path.ts';\n\n// ─── pure path-completion helpers ───────────────────────────────────────────\n\nfunction hasTrailingSeparator(value: string): boolean {\n return value.length > 0 && isSlash(value.charCodeAt(value.length - 1));\n}\n\nfunction resolveFromBase(\n base: string,\n rawValue: string,\n trailingSeparator: boolean,\n): { searchDir: string; prefix: string } {\n const normalizedValue = normalizePath(resolve(base, rawValue));\n if (trailingSeparator) return { searchDir: normalizedValue, prefix: '' };\n return {\n searchDir: dirname(normalizedValue),\n prefix: basename(normalizedValue),\n };\n}\n\nfunction parseNamedRootInput(value: string): { rootName: string; remainder: string } | undefined {\n const normalizedInput = toPosixPath(value);\n if (!normalizedInput) return undefined;\n const slashIndex = normalizedInput.indexOf('/');\n if (slashIndex === -1) return { rootName: normalizedInput, remainder: '' };\n const rootName = normalizedInput.slice(0, slashIndex);\n if (!rootName) return undefined;\n return { rootName, remainder: normalizedInput.slice(slashIndex + 1) };\n}\n\nfunction findAllowedRootByName(rootName: string, allowed: readonly string[]): string | undefined {\n const normalizedRootName = rootName.toLowerCase();\n return allowed.find((candidate) => basename(candidate).toLowerCase() === normalizedRootName);\n}\n\nfunction resolveNamedRootContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const parsed = parseNamedRootInput(currentValue);\n if (!parsed) return undefined;\n const root = findAllowedRootByName(parsed.rootName, allowed);\n if (!root) return undefined;\n const trailingSeparator = hasTrailingSeparator(currentValue);\n return resolveFromBase(root, parsed.remainder, trailingSeparator);\n}\n\nfunction withDirectorySeparator(value: string): string {\n return value.endsWith(sep) ? value : `${value}${sep}`;\n}\n\nfunction collectAllowedRoots(\n allowed: readonly string[],\n predicate: (root: string) => boolean,\n): string[] {\n return allowed.filter(predicate).map(withDirectorySeparator);\n}\n\nfunction findRootPrefixMatches(currentValue: string, allowed: readonly string[]): string[] {\n const rootPrefix = parseNamedRootInput(currentValue)?.rootName.toLowerCase() ?? '';\n return collectAllowedRoots(allowed, (root) =>\n basename(root).toLowerCase().startsWith(rootPrefix),\n );\n}\n\nfunction findMatchingRoots(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n): string[] {\n const lowerPrefix = prefix.toLowerCase();\n const normalizedSearchDir = normalizePath(searchDir);\n return collectAllowedRoots(allowed, (root) => {\n const rootDir = dirname(root);\n if (!isSamePath(rootDir, normalizedSearchDir)) return false;\n return basename(root).toLowerCase().startsWith(lowerPrefix);\n });\n}\n\nfunction mergeCompletionMatches(\n dirMatches: readonly string[],\n rootMatches: readonly string[],\n): string[] {\n const isDir = (v: string): boolean => v.endsWith(sep);\n return [...new Set([...dirMatches, ...rootMatches])].sort(\n (left, right) => Number(isDir(right)) - Number(isDir(left)) || left.localeCompare(right),\n );\n}\n\nasync function findMatchesInDirectory(\n searchDir: string,\n prefix: string,\n pathGuard: PathGuard,\n): Promise<string[]> {\n const matches: string[] = [];\n // The guard owns what a listing may enter: containment through the real\n // path, device names, the sensitive list, is-a-directory. A refusal is\n // \"nothing to list\", never an error, so root suggestions survive it.\n const listable = await pathGuard.validateExistingDirectory(searchDir).then(\n (validated) => validated,\n () => undefined,\n );\n if (listable === undefined) return matches;\n try {\n // Stream via opendir and collect every match. Do not cap here: the\n // SDK's completion result keeps the first 100 values and reports the\n // full length as `total` / `hasMore`, so the list must reach it whole\n // and already sorted (mergeCompletionMatches). Capping here would\n // keep the opendir-first 100, not the alphabetically-first 100, and\n // hide the true count from the client.\n // The directory the guard validated: a spelling that differs from the\n // disk only in Unicode (#101) passes validation but is not on disk as typed.\n const dir = await opendir(listable);\n try {\n const lowerPrefix = prefix.toLowerCase();\n for await (const entry of dir) {\n if (prefix !== '' && !entry.name.toLowerCase().startsWith(lowerPrefix)) continue;\n const fullPath = join(searchDir, entry.name);\n if (pathGuard.isSensitive(fullPath)) continue;\n matches.push(entry.isDirectory() ? `${fullPath}${sep}` : fullPath);\n }\n } finally {\n await dir.close().catch(() => {\n /* dir already closed or opendir never resolved */\n });\n }\n } catch (err) {\n if (!isSkippableErrno(err)) {\n Logger.warn('PathCompleter.findMatchesInDirectory: readdir failed', {\n searchDir,\n error: String(err),\n });\n }\n }\n return matches;\n}\n\nfunction getSearchContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const trailingSeparator = hasTrailingSeparator(currentValue);\n if (isAbsolute(currentValue)) {\n return resolveFromBase(parse(currentValue).root || sep, currentValue, trailingSeparator);\n }\n const namedRootContext = resolveNamedRootContext(currentValue, allowed);\n if (namedRootContext) return namedRootContext;\n if (allowed.length === 1) {\n const base = allowed[0];\n if (base) return resolveFromBase(base, currentValue, trailingSeparator);\n }\n return undefined;\n}\n\n/**\n * Path suggestions for `completion/complete` on the file template. One\n * `opendir` per call; clients debounce, so nothing is cached here. Returns\n * every match, sorted; the SDK truncates to 100 and reports the total.\n */\nexport async function suggestPaths(pathGuard: PathGuard, value: string): Promise<string[]> {\n // Roots to pick, name and list.\n const roots = pathGuard.getRoots();\n\n try {\n if (!value) {\n return roots;\n }\n\n const context = getSearchContext(value, roots);\n if (!context) {\n return findRootPrefixMatches(value, roots);\n }\n\n const { searchDir, prefix } = context;\n const dirMatches = await findMatchesInDirectory(searchDir, prefix, pathGuard);\n const rootMatches = findMatchingRoots(searchDir, prefix, roots);\n return mergeCompletionMatches(dirMatches, rootMatches);\n } catch (error) {\n rethrowIfAborted(error);\n Logger.warn('suggestPaths: completion failed, returning empty list', {\n error: error instanceof Error ? (error.stack ?? error.message) : String(error),\n });\n return [];\n }\n}\n"]}
@@ -29,17 +29,53 @@ export declare const CHAR_COLON = 58;
29
29
  export declare const trimTrailingDotsAndSpaces: (segment: string) => string;
30
30
  export declare function getReservedDeviceNameForPath(requestedPath: string): string | undefined;
31
31
  export declare function isWindowsDriveRelativePath(requestedPath: string): boolean;
32
+ /**
33
+ * True for a path that names a Windows drive: `C:\x`, `C:/x`, `C:`, and the
34
+ * drive-relative `C:x`. Only Windows resolves one. On POSIX it is a relative
35
+ * path, so `C:\x` would become a file of that name under the root.
36
+ */
37
+ export declare const isWindowsDrivePath: (requestedPath: string) => boolean;
38
+ /**
39
+ * A drive path this host refuses before resolving it: drive-relative anywhere,
40
+ * and any drive path where the host is not Windows.
41
+ */
42
+ export declare const isUnresolvableDrivePath: (requestedPath: string) => boolean;
43
+ export declare function expandHome(filepath: string): string;
32
44
  export declare function normalizePath(p: string): string;
33
- export declare function normalizeCaseForComparison(value: string): string;
34
45
  export declare function isSamePath(left: string, right: string): boolean;
35
46
  /**
36
- * On a case-insensitive filesystem a write target that already exists is
37
- * realpathed to its on-disk spelling, so a case-only rename (`foo.txt` ->
38
- * `Foo.txt`) resolves to the source itself. Returns the resolved target
39
- * re-spelled with the requested basename when the two differ only by case;
47
+ * What two names share when they differ only in Unicode form (NFC or NFD) or
48
+ * in which space character they use (U+0020, U+00A0, U+202F, and every other
49
+ * Zs), case-folded where the filesystem folds case. A model types back
50
+ * `caf\u00e9` and `shot PM.png` for the `cafe\u0301` and `shot\u202fPM.png` a
51
+ * listing showed it (#101).
52
+ */
53
+ export declare function unicodeMatchKey(name: string): string;
54
+ /**
55
+ * True when two paths are one path once case (where the filesystem folds it)
56
+ * and Unicode spelling are folded: the spellings the path guard resolves to
57
+ * one entry when only one of them is on disk.
58
+ */
59
+ export declare function isEquivalentPath(left: string, right: string): boolean;
60
+ /**
61
+ * False for a name of printable ASCII with no space, which no other entry can
62
+ * share a `unicodeMatchKey` with — except a name holding U+212A, U+037E or
63
+ * U+1FEF, which NFC maps to ASCII; those stay unmatched. A missed lookup of
64
+ * such a name skips the directory scan, so creating `new-file.ts` costs what
65
+ * it did.
66
+ */
67
+ export declare function mayHaveEquivalentSpelling(name: string): boolean;
68
+ /** `name` with each non-ASCII code unit as a `\uXXXX` escape, which a JSON string argument accepts. */
69
+ export declare function escapeNonAscii(name: string): string;
70
+ /**
71
+ * A write target that already exists is realpathed to its on-disk spelling,
72
+ * so a rename that changes only case (`foo.txt` -> `Foo.txt` on a
73
+ * case-insensitive filesystem) or only Unicode spelling (`cafe\u0301.txt` ->
74
+ * `caf\u00e9.txt`) resolves to the source itself. Returns the resolved target
75
+ * re-spelled with the requested basename when the two differ only that way;
40
76
  * otherwise the resolved target unchanged. Parent segments are not re-spelled.
41
77
  */
42
- export declare function respellCaseOnlyTarget(requestedPath: string, resolvedTarget: string): string;
78
+ export declare function respellSpellingOnlyTarget(requestedPath: string, resolvedTarget: string): string;
43
79
  export declare function normalizeAllowedDirectory(dir: string): string;
44
80
  export declare function isPathInsideDirectory(normalizedDirectory: string, normalizedCandidate: string): boolean;
45
81
  export declare function isPathWithinDirectories(normalizedPath: string, allowedDirs: readonly string[]): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/core/path-utils.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,WAAW,IAAI,OAAO,CAAC;IACvB,MAAM,IAAI,OAAO,CAAC;IAClB,cAAc,IAAI,OAAO,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAK9D;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAClC,IAAI,CAON;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAM9D;AAID,eAAO,MAAM,UAAU,SAA+B,CAAC;AAEvD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OAAqC,CAAC;AAE7E,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OACoB,CAAC;AAE5D,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAAqC,CAAC;AAElF,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhE;AAID,eAAO,MAAM,UAAU,KAAK,CAAC;AA4B7B;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,KAAG,MAAuC,CAAC;AASpG,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAStF;AAED,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAOzE;AAUD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAa/C;AAID,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAO/D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAK3F;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAa7D;AAMD,wBAAgB,qBAAqB,CACnC,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAST;AAED,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAET"}
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/core/path-utils.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,WAAW,IAAI,OAAO,CAAC;IACvB,MAAM,IAAI,OAAO,CAAC;IAClB,cAAc,IAAI,OAAO,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAK9D;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAClC,IAAI,CAON;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAM9D;AAID,eAAO,MAAM,UAAU,SAA+B,CAAC;AAEvD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OAAqC,CAAC;AAE7E,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OACoB,CAAC;AAE5D,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAAqC,CAAC;AAElF,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhE;AAID,eAAO,MAAM,UAAU,KAAK,CAAC;AA4B7B;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,KAAG,MAAuC,CAAC;AASpG,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAStF;AAED,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAOzE;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,eAAe,MAAM,KAAG,OACN,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAI,eAAe,MAAM,KAAG,OAC4B,CAAC;AAE7F,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAa/C;AAQD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAO/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIrE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAO/F;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAa7D;AAMD,wBAAgB,qBAAqB,CACnC,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAST;AAED,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAET"}
@@ -97,7 +97,18 @@ export function isWindowsDriveRelativePath(requestedPath) {
97
97
  const { root } = win32.parse(requestedPath);
98
98
  return root.length === 2;
99
99
  }
100
- function expandHome(filepath) {
100
+ /**
101
+ * True for a path that names a Windows drive: `C:\x`, `C:/x`, `C:`, and the
102
+ * drive-relative `C:x`. Only Windows resolves one. On POSIX it is a relative
103
+ * path, so `C:\x` would become a file of that name under the root.
104
+ */
105
+ export const isWindowsDrivePath = (requestedPath) => /^[A-Za-z]:/u.test(win32.parse(requestedPath).root);
106
+ /**
107
+ * A drive path this host refuses before resolving it: drive-relative anywhere,
108
+ * and any drive path where the host is not Windows.
109
+ */
110
+ export const isUnresolvableDrivePath = (requestedPath) => IS_WINDOWS ? isWindowsDriveRelativePath(requestedPath) : isWindowsDrivePath(requestedPath);
111
+ export function expandHome(filepath) {
101
112
  if (filepath === '~' || filepath.startsWith('~/') || filepath.startsWith('~\\')) {
102
113
  const rest = filepath.slice(1).replace(/^[/\\]+/, '');
103
114
  return rest ? join(HOMEDIR, rest) : HOMEDIR;
@@ -117,7 +128,7 @@ export function normalizePath(p) {
117
128
  return resolved;
118
129
  }
119
130
  const IS_CASE_INSENSITIVE_FS = IS_WINDOWS || platform() === 'darwin';
120
- export function normalizeCaseForComparison(value) {
131
+ function normalizeCaseForComparison(value) {
121
132
  return IS_CASE_INSENSITIVE_FS ? value.toLowerCase() : value;
122
133
  }
123
134
  export function isSamePath(left, right) {
@@ -129,18 +140,53 @@ export function isSamePath(left, right) {
129
140
  return leftResolved === rightResolved;
130
141
  }
131
142
  /**
132
- * On a case-insensitive filesystem a write target that already exists is
133
- * realpathed to its on-disk spelling, so a case-only rename (`foo.txt` ->
134
- * `Foo.txt`) resolves to the source itself. Returns the resolved target
135
- * re-spelled with the requested basename when the two differ only by case;
143
+ * What two names share when they differ only in Unicode form (NFC or NFD) or
144
+ * in which space character they use (U+0020, U+00A0, U+202F, and every other
145
+ * Zs), case-folded where the filesystem folds case. A model types back
146
+ * `caf\u00e9` and `shot PM.png` for the `cafe\u0301` and `shot\u202fPM.png` a
147
+ * listing showed it (#101).
148
+ */
149
+ export function unicodeMatchKey(name) {
150
+ return normalizeCaseForComparison(name.normalize('NFC').replace(/\p{Zs}/gu, ' '));
151
+ }
152
+ /**
153
+ * True when two paths are one path once case (where the filesystem folds it)
154
+ * and Unicode spelling are folded: the spellings the path guard resolves to
155
+ * one entry when only one of them is on disk.
156
+ */
157
+ export function isEquivalentPath(left, right) {
158
+ return (isSamePath(left, right) || unicodeMatchKey(resolve(left)) === unicodeMatchKey(resolve(right)));
159
+ }
160
+ /**
161
+ * False for a name of printable ASCII with no space, which no other entry can
162
+ * share a `unicodeMatchKey` with — except a name holding U+212A, U+037E or
163
+ * U+1FEF, which NFC maps to ASCII; those stay unmatched. A missed lookup of
164
+ * such a name skips the directory scan, so creating `new-file.ts` costs what
165
+ * it did.
166
+ */
167
+ export function mayHaveEquivalentSpelling(name) {
168
+ return /[^\x21-\x7e]/u.test(name);
169
+ }
170
+ /** `name` with each non-ASCII code unit as a `\uXXXX` escape, which a JSON string argument accepts. */
171
+ export function escapeNonAscii(name) {
172
+ return name.replace(/[^\x20-\x7e]/gu, (char) => Array.from({ length: char.length }, (_, index) => `\\u${char.charCodeAt(index).toString(16).padStart(4, '0')}`).join(''));
173
+ }
174
+ /**
175
+ * A write target that already exists is realpathed to its on-disk spelling,
176
+ * so a rename that changes only case (`foo.txt` -> `Foo.txt` on a
177
+ * case-insensitive filesystem) or only Unicode spelling (`cafe\u0301.txt` ->
178
+ * `caf\u00e9.txt`) resolves to the source itself. Returns the resolved target
179
+ * re-spelled with the requested basename when the two differ only that way;
136
180
  * otherwise the resolved target unchanged. Parent segments are not re-spelled.
137
181
  */
138
- export function respellCaseOnlyTarget(requestedPath, resolvedTarget) {
182
+ export function respellSpellingOnlyTarget(requestedPath, resolvedTarget) {
139
183
  const requestedName = basename(requestedPath);
140
- if (requestedName === basename(resolvedTarget))
184
+ const resolvedName = basename(resolvedTarget);
185
+ if (requestedName === resolvedName)
141
186
  return resolvedTarget;
142
- const respelled = join(dirname(resolvedTarget), requestedName);
143
- return isSamePath(respelled, resolvedTarget) ? respelled : resolvedTarget;
187
+ return unicodeMatchKey(requestedName) === unicodeMatchKey(resolvedName)
188
+ ? join(dirname(resolvedTarget), requestedName)
189
+ : resolvedTarget;
144
190
  }
145
191
  export function normalizeAllowedDirectory(dir) {
146
192
  const trimmed = dir.trim();
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/core/path-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { homedir, platform } from 'node:os';\nimport {\n basename,\n delimiter,\n dirname,\n join,\n parse,\n relative,\n resolve,\n sep,\n win32,\n} from 'node:path';\n\n// ─── Shared primitives (no intra-package imports; every core module may import this) ──\n\nexport type EntryType = 'file' | 'directory' | 'symlink' | 'other';\n\n/** The three predicates both `Dirent` and `Stats` expose - all `resolveEntryType` needs. */\nexport interface DirentLike {\n isDirectory(): boolean;\n isFile(): boolean;\n isSymbolicLink(): boolean;\n}\n\nexport function resolveEntryType(dirent: DirentLike): EntryType {\n if (dirent.isDirectory()) return 'directory';\n if (dirent.isFile()) return 'file';\n if (dirent.isSymbolicLink()) return 'symlink';\n return 'other';\n}\n\nconst warnedSettings = new Set<string>();\n\n/**\n * Warn once per (setting, value) that a configured value was rejected, and say\n * what was used instead. console.error, not Logger: this module stays\n * dependency-free to avoid import cycles, and a startup misconfiguration is\n * worth printing even when the operator asked for errors only.\n */\nexport function warnInvalidSetting(\n name: string,\n value: string,\n expected: string,\n fallback: string | number | boolean,\n): void {\n const key = `${name}:${value}`;\n if (warnedSettings.has(key)) return;\n warnedSettings.add(key);\n console.error(\n `[warning] Invalid ${name} value: ${value} (must be ${expected}). Using default: ${String(fallback)}`,\n );\n}\n\nexport function splitDirList(val: string | undefined): string[] {\n if (!val) return [];\n return val\n .split(delimiter)\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n}\n\n// ─── Character-level path primitives ─────────────────────────────────────────\n\nexport const IS_WINDOWS = process.platform === 'win32';\n\nexport const isSlash = (code: number): boolean => code === 47 || code === 92;\n\nexport const isAlpha = (code: number): boolean =>\n (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n\nexport const toPosixPath = (value: string): string => value.replaceAll('\\\\', '/');\n\n/** `path.relative` with forward slashes, so displayed paths match across platforms. */\nexport function toPosixRelative(from: string, to: string): string {\n return toPosixPath(relative(from, to));\n}\n\n// ─── Path normalization and containment ──────────────────────────────────────\n\nexport const CHAR_COLON = 58;\nconst HOMEDIR = homedir();\n\nconst RESERVED_DEVICE_NAMES = new Set<string>([\n 'CON',\n 'PRN',\n 'AUX',\n 'NUL',\n 'COM1',\n 'COM2',\n 'COM3',\n 'COM4',\n 'COM5',\n 'COM6',\n 'COM7',\n 'COM8',\n 'COM9',\n 'LPT1',\n 'LPT2',\n 'LPT3',\n 'LPT4',\n 'LPT5',\n 'LPT6',\n 'LPT7',\n 'LPT8',\n 'LPT9',\n]);\n\n/**\n * Win32 strips trailing dots and spaces at the syscall boundary (a request for\n * `.env ` creates `.env`), so every name check must trim them first or it is\n * bypassed on Windows.\n */\nexport const trimTrailingDotsAndSpaces = (segment: string): string => segment.replace(/[. ]+$/, '');\n\nfunction getReservedDeviceName(segment: string): string | undefined {\n const trimmed = trimTrailingDotsAndSpaces(segment);\n const withoutStream = trimmed.split(':')[0] ?? '';\n const baseName = (withoutStream.split('.')[0] ?? '').toUpperCase();\n return RESERVED_DEVICE_NAMES.has(baseName) ? baseName : undefined;\n}\n\nexport function getReservedDeviceNameForPath(requestedPath: string): string | undefined {\n const segments = requestedPath.split(/[\\\\/]/u);\n for (const segment of segments) {\n const reserved = getReservedDeviceName(segment);\n if (reserved) {\n return reserved;\n }\n }\n return undefined;\n}\n\nexport function isWindowsDriveRelativePath(requestedPath: string): boolean {\n // Check on all platforms so cross-platform clients cannot smuggle drive-relative\n // inputs (e.g. C:relative) to a POSIX-hosted server where path.resolve would\n // silently expand them relative to CWD. win32.parse is the platform-independent\n // binding, so this reads the same on a POSIX host.\n const { root } = win32.parse(requestedPath);\n return root.length === 2;\n}\n\nfunction expandHome(filepath: string): string {\n if (filepath === '~' || filepath.startsWith('~/') || filepath.startsWith('~\\\\')) {\n const rest = filepath.slice(1).replace(/^[/\\\\]+/, '');\n return rest ? join(HOMEDIR, rest) : HOMEDIR;\n }\n return filepath;\n}\n\nexport function normalizePath(p: string): string {\n const resolved = resolve(expandHome(p));\n\n // On Windows only the drive letter is lowercased (e.g. \"C:\\Foo\\Bar\").\n // The rest of the path retains its original casing.\n // IMPORTANT: callers must use isSamePath / isPathInsideDirectory for all\n // equality and containment checks — never raw string equality — because\n // those helpers apply full case-folding before comparing.\n if (IS_WINDOWS && resolved.length >= 2 && resolved.charCodeAt(1) === CHAR_COLON) {\n return resolved.charAt(0).toLowerCase() + resolved.slice(1);\n }\n\n return resolved;\n}\n\nconst IS_CASE_INSENSITIVE_FS = IS_WINDOWS || platform() === 'darwin';\n\nexport function normalizeCaseForComparison(value: string): string {\n return IS_CASE_INSENSITIVE_FS ? value.toLowerCase() : value;\n}\n\nexport function isSamePath(left: string, right: string): boolean {\n if (left === right) {\n return true;\n }\n const leftResolved = normalizeCaseForComparison(resolve(left));\n const rightResolved = normalizeCaseForComparison(resolve(right));\n return leftResolved === rightResolved;\n}\n\n/**\n * On a case-insensitive filesystem a write target that already exists is\n * realpathed to its on-disk spelling, so a case-only rename (`foo.txt` ->\n * `Foo.txt`) resolves to the source itself. Returns the resolved target\n * re-spelled with the requested basename when the two differ only by case;\n * otherwise the resolved target unchanged. Parent segments are not re-spelled.\n */\nexport function respellCaseOnlyTarget(requestedPath: string, resolvedTarget: string): string {\n const requestedName = basename(requestedPath);\n if (requestedName === basename(resolvedTarget)) return resolvedTarget;\n const respelled = join(dirname(resolvedTarget), requestedName);\n return isSamePath(respelled, resolvedTarget) ? respelled : resolvedTarget;\n}\n\nexport function normalizeAllowedDirectory(dir: string): string {\n const trimmed = dir.trim();\n if (trimmed.length === 0) return '';\n\n const normalized = normalizePath(trimmed);\n const { root } = parse(normalized);\n\n // Keep filesystem roots as-is (\"/\", \"c:\\\\\", \"\\\\\\\\server\\\\share\\\\\").\n if (isSamePath(normalized, root)) {\n return root;\n }\n\n return normalized.length > 1 && normalized.endsWith(sep) ? normalized.slice(0, -1) : normalized;\n}\n\n// `\\` separates path segments only on Windows. On POSIX it is a filename\n// character, so `/root\\x` names a sibling of `/root`, not a child.\nconst isPathSeparator = (code: number): boolean => code === 47 || (IS_WINDOWS && code === 92);\n\nexport function isPathInsideDirectory(\n normalizedDirectory: string,\n normalizedCandidate: string,\n): boolean {\n const root = normalizeCaseForComparison(normalizedDirectory);\n const candidate = normalizeCaseForComparison(normalizedCandidate);\n\n if (root === candidate) return true;\n if (!candidate.startsWith(root)) return false;\n\n if (isPathSeparator(root.charCodeAt(root.length - 1))) return true;\n return isPathSeparator(candidate.charCodeAt(root.length));\n}\n\nexport function isPathWithinDirectories(\n normalizedPath: string,\n allowedDirs: readonly string[],\n): boolean {\n return allowedDirs.some((dir) => isPathInsideDirectory(dir, normalizedPath));\n}\n"]}
1
+ {"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/core/path-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { homedir, platform } from 'node:os';\nimport {\n basename,\n delimiter,\n dirname,\n join,\n parse,\n relative,\n resolve,\n sep,\n win32,\n} from 'node:path';\n\n// ─── Shared primitives (no intra-package imports; every core module may import this) ──\n\nexport type EntryType = 'file' | 'directory' | 'symlink' | 'other';\n\n/** The three predicates both `Dirent` and `Stats` expose - all `resolveEntryType` needs. */\nexport interface DirentLike {\n isDirectory(): boolean;\n isFile(): boolean;\n isSymbolicLink(): boolean;\n}\n\nexport function resolveEntryType(dirent: DirentLike): EntryType {\n if (dirent.isDirectory()) return 'directory';\n if (dirent.isFile()) return 'file';\n if (dirent.isSymbolicLink()) return 'symlink';\n return 'other';\n}\n\nconst warnedSettings = new Set<string>();\n\n/**\n * Warn once per (setting, value) that a configured value was rejected, and say\n * what was used instead. console.error, not Logger: this module stays\n * dependency-free to avoid import cycles, and a startup misconfiguration is\n * worth printing even when the operator asked for errors only.\n */\nexport function warnInvalidSetting(\n name: string,\n value: string,\n expected: string,\n fallback: string | number | boolean,\n): void {\n const key = `${name}:${value}`;\n if (warnedSettings.has(key)) return;\n warnedSettings.add(key);\n console.error(\n `[warning] Invalid ${name} value: ${value} (must be ${expected}). Using default: ${String(fallback)}`,\n );\n}\n\nexport function splitDirList(val: string | undefined): string[] {\n if (!val) return [];\n return val\n .split(delimiter)\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n}\n\n// ─── Character-level path primitives ─────────────────────────────────────────\n\nexport const IS_WINDOWS = process.platform === 'win32';\n\nexport const isSlash = (code: number): boolean => code === 47 || code === 92;\n\nexport const isAlpha = (code: number): boolean =>\n (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n\nexport const toPosixPath = (value: string): string => value.replaceAll('\\\\', '/');\n\n/** `path.relative` with forward slashes, so displayed paths match across platforms. */\nexport function toPosixRelative(from: string, to: string): string {\n return toPosixPath(relative(from, to));\n}\n\n// ─── Path normalization and containment ──────────────────────────────────────\n\nexport const CHAR_COLON = 58;\nconst HOMEDIR = homedir();\n\nconst RESERVED_DEVICE_NAMES = new Set<string>([\n 'CON',\n 'PRN',\n 'AUX',\n 'NUL',\n 'COM1',\n 'COM2',\n 'COM3',\n 'COM4',\n 'COM5',\n 'COM6',\n 'COM7',\n 'COM8',\n 'COM9',\n 'LPT1',\n 'LPT2',\n 'LPT3',\n 'LPT4',\n 'LPT5',\n 'LPT6',\n 'LPT7',\n 'LPT8',\n 'LPT9',\n]);\n\n/**\n * Win32 strips trailing dots and spaces at the syscall boundary (a request for\n * `.env ` creates `.env`), so every name check must trim them first or it is\n * bypassed on Windows.\n */\nexport const trimTrailingDotsAndSpaces = (segment: string): string => segment.replace(/[. ]+$/, '');\n\nfunction getReservedDeviceName(segment: string): string | undefined {\n const trimmed = trimTrailingDotsAndSpaces(segment);\n const withoutStream = trimmed.split(':')[0] ?? '';\n const baseName = (withoutStream.split('.')[0] ?? '').toUpperCase();\n return RESERVED_DEVICE_NAMES.has(baseName) ? baseName : undefined;\n}\n\nexport function getReservedDeviceNameForPath(requestedPath: string): string | undefined {\n const segments = requestedPath.split(/[\\\\/]/u);\n for (const segment of segments) {\n const reserved = getReservedDeviceName(segment);\n if (reserved) {\n return reserved;\n }\n }\n return undefined;\n}\n\nexport function isWindowsDriveRelativePath(requestedPath: string): boolean {\n // Check on all platforms so cross-platform clients cannot smuggle drive-relative\n // inputs (e.g. C:relative) to a POSIX-hosted server where path.resolve would\n // silently expand them relative to CWD. win32.parse is the platform-independent\n // binding, so this reads the same on a POSIX host.\n const { root } = win32.parse(requestedPath);\n return root.length === 2;\n}\n\n/**\n * True for a path that names a Windows drive: `C:\\x`, `C:/x`, `C:`, and the\n * drive-relative `C:x`. Only Windows resolves one. On POSIX it is a relative\n * path, so `C:\\x` would become a file of that name under the root.\n */\nexport const isWindowsDrivePath = (requestedPath: string): boolean =>\n /^[A-Za-z]:/u.test(win32.parse(requestedPath).root);\n\n/**\n * A drive path this host refuses before resolving it: drive-relative anywhere,\n * and any drive path where the host is not Windows.\n */\nexport const isUnresolvableDrivePath = (requestedPath: string): boolean =>\n IS_WINDOWS ? isWindowsDriveRelativePath(requestedPath) : isWindowsDrivePath(requestedPath);\n\nexport function expandHome(filepath: string): string {\n if (filepath === '~' || filepath.startsWith('~/') || filepath.startsWith('~\\\\')) {\n const rest = filepath.slice(1).replace(/^[/\\\\]+/, '');\n return rest ? join(HOMEDIR, rest) : HOMEDIR;\n }\n return filepath;\n}\n\nexport function normalizePath(p: string): string {\n const resolved = resolve(expandHome(p));\n\n // On Windows only the drive letter is lowercased (e.g. \"C:\\Foo\\Bar\").\n // The rest of the path retains its original casing.\n // IMPORTANT: callers must use isSamePath / isPathInsideDirectory for all\n // equality and containment checks — never raw string equality — because\n // those helpers apply full case-folding before comparing.\n if (IS_WINDOWS && resolved.length >= 2 && resolved.charCodeAt(1) === CHAR_COLON) {\n return resolved.charAt(0).toLowerCase() + resolved.slice(1);\n }\n\n return resolved;\n}\n\nconst IS_CASE_INSENSITIVE_FS = IS_WINDOWS || platform() === 'darwin';\n\nfunction normalizeCaseForComparison(value: string): string {\n return IS_CASE_INSENSITIVE_FS ? value.toLowerCase() : value;\n}\n\nexport function isSamePath(left: string, right: string): boolean {\n if (left === right) {\n return true;\n }\n const leftResolved = normalizeCaseForComparison(resolve(left));\n const rightResolved = normalizeCaseForComparison(resolve(right));\n return leftResolved === rightResolved;\n}\n\n/**\n * What two names share when they differ only in Unicode form (NFC or NFD) or\n * in which space character they use (U+0020, U+00A0, U+202F, and every other\n * Zs), case-folded where the filesystem folds case. A model types back\n * `caf\\u00e9` and `shot PM.png` for the `cafe\\u0301` and `shot\\u202fPM.png` a\n * listing showed it (#101).\n */\nexport function unicodeMatchKey(name: string): string {\n return normalizeCaseForComparison(name.normalize('NFC').replace(/\\p{Zs}/gu, ' '));\n}\n\n/**\n * True when two paths are one path once case (where the filesystem folds it)\n * and Unicode spelling are folded: the spellings the path guard resolves to\n * one entry when only one of them is on disk.\n */\nexport function isEquivalentPath(left: string, right: string): boolean {\n return (\n isSamePath(left, right) || unicodeMatchKey(resolve(left)) === unicodeMatchKey(resolve(right))\n );\n}\n\n/**\n * False for a name of printable ASCII with no space, which no other entry can\n * share a `unicodeMatchKey` with — except a name holding U+212A, U+037E or\n * U+1FEF, which NFC maps to ASCII; those stay unmatched. A missed lookup of\n * such a name skips the directory scan, so creating `new-file.ts` costs what\n * it did.\n */\nexport function mayHaveEquivalentSpelling(name: string): boolean {\n return /[^\\x21-\\x7e]/u.test(name);\n}\n\n/** `name` with each non-ASCII code unit as a `\\uXXXX` escape, which a JSON string argument accepts. */\nexport function escapeNonAscii(name: string): string {\n return name.replace(/[^\\x20-\\x7e]/gu, (char) =>\n Array.from(\n { length: char.length },\n (_, index) => `\\\\u${char.charCodeAt(index).toString(16).padStart(4, '0')}`,\n ).join(''),\n );\n}\n\n/**\n * A write target that already exists is realpathed to its on-disk spelling,\n * so a rename that changes only case (`foo.txt` -> `Foo.txt` on a\n * case-insensitive filesystem) or only Unicode spelling (`cafe\\u0301.txt` ->\n * `caf\\u00e9.txt`) resolves to the source itself. Returns the resolved target\n * re-spelled with the requested basename when the two differ only that way;\n * otherwise the resolved target unchanged. Parent segments are not re-spelled.\n */\nexport function respellSpellingOnlyTarget(requestedPath: string, resolvedTarget: string): string {\n const requestedName = basename(requestedPath);\n const resolvedName = basename(resolvedTarget);\n if (requestedName === resolvedName) return resolvedTarget;\n return unicodeMatchKey(requestedName) === unicodeMatchKey(resolvedName)\n ? join(dirname(resolvedTarget), requestedName)\n : resolvedTarget;\n}\n\nexport function normalizeAllowedDirectory(dir: string): string {\n const trimmed = dir.trim();\n if (trimmed.length === 0) return '';\n\n const normalized = normalizePath(trimmed);\n const { root } = parse(normalized);\n\n // Keep filesystem roots as-is (\"/\", \"c:\\\\\", \"\\\\\\\\server\\\\share\\\\\").\n if (isSamePath(normalized, root)) {\n return root;\n }\n\n return normalized.length > 1 && normalized.endsWith(sep) ? normalized.slice(0, -1) : normalized;\n}\n\n// `\\` separates path segments only on Windows. On POSIX it is a filename\n// character, so `/root\\x` names a sibling of `/root`, not a child.\nconst isPathSeparator = (code: number): boolean => code === 47 || (IS_WINDOWS && code === 92);\n\nexport function isPathInsideDirectory(\n normalizedDirectory: string,\n normalizedCandidate: string,\n): boolean {\n const root = normalizeCaseForComparison(normalizedDirectory);\n const candidate = normalizeCaseForComparison(normalizedCandidate);\n\n if (root === candidate) return true;\n if (!candidate.startsWith(root)) return false;\n\n if (isPathSeparator(root.charCodeAt(root.length - 1))) return true;\n return isPathSeparator(candidate.charCodeAt(root.length));\n}\n\nexport function isPathWithinDirectories(\n normalizedPath: string,\n allowedDirs: readonly string[],\n): boolean {\n return allowedDirs.some((dir) => isPathInsideDirectory(dir, normalizedPath));\n}\n"]}