@lzehrung/codegraph 2.3.8 → 2.3.10

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 (81) hide show
  1. package/codegraph-skill/codegraph/SKILL.md +3 -0
  2. package/dist/agent/query-index/content.d.ts.map +1 -1
  3. package/dist/agent/query-index/content.js +8 -4
  4. package/dist/agent/query-index/content.js.map +1 -1
  5. package/dist/agent/renamePreview.js +4 -3
  6. package/dist/agent/renamePreview.js.map +1 -1
  7. package/dist/agent/session.d.ts.map +1 -1
  8. package/dist/agent/session.js +33 -2
  9. package/dist/agent/session.js.map +1 -1
  10. package/dist/bin/cli.js +498 -145
  11. package/dist/bin/queryIndexWorker.js +32 -13
  12. package/dist/documentLinks/check.d.ts.map +1 -1
  13. package/dist/documentLinks/check.js +5 -3
  14. package/dist/documentLinks/check.js.map +1 -1
  15. package/dist/duplicates/units.d.ts.map +1 -1
  16. package/dist/duplicates/units.js +23 -8
  17. package/dist/duplicates/units.js.map +1 -1
  18. package/dist/graph-builder.d.ts.map +1 -1
  19. package/dist/graph-builder.js +2 -2
  20. package/dist/graph-builder.js.map +1 -1
  21. package/dist/graph-edge-collector.d.ts.map +1 -1
  22. package/dist/graph-edge-collector.js +19 -14
  23. package/dist/graph-edge-collector.js.map +1 -1
  24. package/dist/graphs/unresolved.d.ts.map +1 -1
  25. package/dist/graphs/unresolved.js +12 -4
  26. package/dist/graphs/unresolved.js.map +1 -1
  27. package/dist/impact/callCompatibility.js +2 -2
  28. package/dist/impact/callCompatibility.js.map +1 -1
  29. package/dist/impact/map.js +5 -5
  30. package/dist/impact/map.js.map +1 -1
  31. package/dist/indexer/build-cache/module-cache.js +2 -2
  32. package/dist/indexer/build-cache/module-cache.js.map +1 -1
  33. package/dist/indexer/build-index.d.ts +5 -2
  34. package/dist/indexer/build-index.d.ts.map +1 -1
  35. package/dist/indexer/build-index.js +25 -7
  36. package/dist/indexer/build-index.js.map +1 -1
  37. package/dist/indexer/build-workers.d.ts +4 -0
  38. package/dist/indexer/build-workers.d.ts.map +1 -1
  39. package/dist/indexer/build-workers.js +9 -5
  40. package/dist/indexer/build-workers.js.map +1 -1
  41. package/dist/indexer/navigation-references.d.ts.map +1 -1
  42. package/dist/indexer/navigation-references.js +2 -2
  43. package/dist/indexer/navigation-references.js.map +1 -1
  44. package/dist/indexer/navigation-resolve.d.ts.map +1 -1
  45. package/dist/indexer/navigation-resolve.js +77 -66
  46. package/dist/indexer/navigation-resolve.js.map +1 -1
  47. package/dist/indexer/navigation.d.ts.map +1 -1
  48. package/dist/indexer/navigation.js +6 -4
  49. package/dist/indexer/navigation.js.map +1 -1
  50. package/dist/indexer/parse-context.d.ts +1 -1
  51. package/dist/indexer/parse-context.d.ts.map +1 -1
  52. package/dist/indexer/parse-context.js +6 -2
  53. package/dist/indexer/parse-context.js.map +1 -1
  54. package/dist/indexer/reference-context.d.ts.map +1 -1
  55. package/dist/indexer/reference-context.js +2 -2
  56. package/dist/indexer/reference-context.js.map +1 -1
  57. package/dist/indexer/types.d.ts +6 -0
  58. package/dist/indexer/types.d.ts.map +1 -1
  59. package/dist/languages/filePrep.d.ts +11 -6
  60. package/dist/languages/filePrep.d.ts.map +1 -1
  61. package/dist/languages/filePrep.js +1 -1
  62. package/dist/languages/filePrep.js.map +1 -1
  63. package/dist/languages.d.ts +16 -0
  64. package/dist/languages.d.ts.map +1 -1
  65. package/dist/languages.js +41 -8
  66. package/dist/languages.js.map +1 -1
  67. package/dist/review/risk.js +2 -2
  68. package/dist/review/risk.js.map +1 -1
  69. package/dist/sql/sourceGraph.d.ts.map +1 -1
  70. package/dist/sql/sourceGraph.js +2 -2
  71. package/dist/sql/sourceGraph.js.map +1 -1
  72. package/dist/util/lazySymbols.js +2 -2
  73. package/dist/util/lazySymbols.js.map +1 -1
  74. package/dist/util/projectFiles.d.ts.map +1 -1
  75. package/dist/util/projectFiles.js +40 -27
  76. package/dist/util/projectFiles.js.map +1 -1
  77. package/dist/util/specifiers.d.ts +12 -0
  78. package/dist/util/specifiers.d.ts.map +1 -1
  79. package/dist/util/specifiers.js +271 -0
  80. package/dist/util/specifiers.js.map +1 -1
  81. package/package.json +3 -3
package/dist/bin/cli.js CHANGED
@@ -4584,7 +4584,9 @@ __export(languages_exports, {
4584
4584
  languageExtensionPatterns: () => languageExtensionPatterns,
4585
4585
  normalizeLanguageExtensions: () => normalizeLanguageExtensions,
4586
4586
  supportById: () => supportById,
4587
- supportForFile: () => supportForFile
4587
+ supportForFile: () => supportForFile,
4588
+ supportForFileWithSource: () => supportForFileWithSource,
4589
+ supportForFileWithoutHeaderSample: () => supportForFileWithoutHeaderSample
4588
4590
  });
4589
4591
  import fs5 from "node:fs";
4590
4592
  import path4 from "node:path";
@@ -4628,6 +4630,8 @@ function languageExtensionPatterns(extensions) {
4628
4630
  return Object.keys(normalizeLanguageExtensions(extensions) ?? {}).map(caseInsensitiveExtensionPattern);
4629
4631
  }
4630
4632
  function mappedSupportForFile(filename, extensionMap) {
4633
+ if (!extensionMap)
4634
+ return void 0;
4631
4635
  const mappings = Object.entries(normalizeLanguageExtensions(extensionMap) ?? {}).sort((left, right) => right[0].length - left[0].length || left[0].localeCompare(right[0]));
4632
4636
  const lowerFilename = filename.toLowerCase();
4633
4637
  for (const [extension, languageId] of mappings) {
@@ -4637,19 +4641,36 @@ function mappedSupportForFile(filename, extensionMap) {
4637
4641
  }
4638
4642
  return void 0;
4639
4643
  }
4640
- function supportForFile(filename, extensionMap) {
4641
- const mapped = extensionMap ? mappedSupportForFile(filename, extensionMap) : void 0;
4644
+ function supportForFileWithoutHeaderSample(filename, extensionMap) {
4645
+ const mapped = mappedSupportForFile(filename, extensionMap);
4642
4646
  if (mapped)
4643
4647
  return mapped;
4644
4648
  const ext = path4.extname(filename).toLowerCase();
4645
- if (ext === ".h") {
4646
- const sample = readFileSample(filename);
4647
- if (sample && isLikelyCppHeader(sample))
4648
- return CPP_SUPPORT;
4649
- return C_SUPPORT;
4650
- }
4651
4649
  return LANGUAGE_SUPPORTS.find((s) => s.matchExts.includes(ext));
4652
4650
  }
4651
+ function supportForFile(filename, extensionMap) {
4652
+ if (path4.extname(filename).toLowerCase() !== ".h") {
4653
+ return supportForFileWithoutHeaderSample(filename, extensionMap);
4654
+ }
4655
+ const mapped = mappedSupportForFile(filename, extensionMap);
4656
+ if (mapped)
4657
+ return mapped;
4658
+ const sample = readFileSample(filename);
4659
+ if (sample && isLikelyCppHeader(sample))
4660
+ return CPP_SUPPORT;
4661
+ return C_SUPPORT;
4662
+ }
4663
+ function supportForFileWithSource(filename, source, extensionMap) {
4664
+ if (path4.extname(filename).toLowerCase() !== ".h") {
4665
+ return supportForFileWithoutHeaderSample(filename, extensionMap);
4666
+ }
4667
+ const mapped = mappedSupportForFile(filename, extensionMap);
4668
+ if (mapped)
4669
+ return mapped;
4670
+ if (isLikelyCppHeader(source.slice(0, HEADER_SAMPLE_SIZE)))
4671
+ return CPP_SUPPORT;
4672
+ return C_SUPPORT;
4673
+ }
4653
4674
  function supportById(id) {
4654
4675
  return LANGUAGE_SUPPORTS.find((s) => s.id === id);
4655
4676
  }
@@ -14546,31 +14567,36 @@ async function listProjectFilesInternal(projectRoot, patterns = DEFAULT_PROJECT_
14546
14567
  filterIgnoreGlobs: userIgnoreGlobs,
14547
14568
  resolvedSafeSymlinkDirectories: safeSymlinkDirectories
14548
14569
  });
14549
- const rootSafeFiles = await filterRealPathsWithinRootEntries([...files, ...includedOverrideFiles, ...linkedFiles, ...linkedOverrideFiles], realRoot, reportSourcePathChecks);
14570
+ const candidatePaths = [...files, ...includedOverrideFiles, ...linkedFiles, ...linkedOverrideFiles];
14550
14571
  const seen = /* @__PURE__ */ new Set();
14551
- return rootSafeFiles.map(({ path: filePath, realPath }) => ({ filePath: normalizePath(filePath), realPath })).filter(({ filePath, realPath }) => {
14572
+ const indexableCandidates = [];
14573
+ for (const candidatePath of candidatePaths) {
14574
+ const filePath = normalizePath(candidatePath);
14552
14575
  if (seen.has(filePath))
14553
- return false;
14576
+ continue;
14554
14577
  seen.add(filePath);
14555
14578
  const rootRelative = normalizePath(path9.relative(root, filePath));
14556
14579
  if (!isRelativePathInside(rootRelative))
14557
- return false;
14580
+ continue;
14558
14581
  if (!patternMatchers.some((matcher) => matcher(rootRelative)))
14559
- return false;
14560
- const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher));
14561
- if (!matchesInclude)
14562
- return false;
14563
- if (userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher))) {
14564
- return false;
14565
- }
14566
- const ignoredByDefault = defaultIgnoreMatchers.some((matcher) => matcher(rootRelative));
14567
- if (ignoredByDefault && !(includeMatchers.length > 0 && matchesInclude)) {
14568
- return false;
14582
+ continue;
14583
+ if (includeMatchers.length && !includeMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher))) {
14584
+ continue;
14569
14585
  }
14586
+ if (userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher)))
14587
+ continue;
14588
+ if (!includeMatchers.length && defaultIgnoreMatchers.some((matcher) => matcher(rootRelative)))
14589
+ continue;
14570
14590
  if (isIgnoredByGitignore(filePath, gitignoreIndex))
14571
- return false;
14572
- return normalizePath(realPath) === filePath || !isIgnoredByGitignore(realPath, gitignoreIndex);
14573
- }).map(({ filePath }) => filePath);
14591
+ continue;
14592
+ indexableCandidates.push(filePath);
14593
+ }
14594
+ const rootSafeFiles = await filterRealPathsWithinRootEntries(indexableCandidates, realRoot, reportSourcePathChecks);
14595
+ return rootSafeFiles.filter(({ path: filePath, realPath }) => (
14596
+ // The real path only differs when the entry was reached through a symlink, so
14597
+ // testing it again for every ordinary file doubled the matcher work for nothing.
14598
+ normalizePath(realPath) === filePath || !isIgnoredByGitignore(realPath, gitignoreIndex)
14599
+ )).map(({ path: filePath }) => filePath);
14574
14600
  } catch (error51) {
14575
14601
  logWithLevel(options?.logLevel, "debug", `listProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
14576
14602
  throw new Error(`Failed to list files in ${root}: ${stringifyUnknown(error51)}`);
@@ -14789,10 +14815,10 @@ async function discoverProjectFilesInternal(projectRoot, options) {
14789
14815
  onlyFiles: false,
14790
14816
  resolvedSafeSymlinkDirectories: safeSymlinkDirectories
14791
14817
  });
14792
- const rootSafeCandidateEntries = await filterRealPathsWithinRootEntries(gitCandidates.files, realRoot, reportMetadataFileChecks);
14793
- const filteredFiles = filterGitIgnoredEntries(rootSafeCandidateEntries);
14794
- const fileMatches = filteredFiles.filter((file2) => PROJECT_FILE_DEFINITIONS.some((definition, definitionIndex) => definition.kind === "file" && matchesDefinition(path9.basename(file2), definitionIndex)));
14795
- const rawCandidateDirectories = collectCandidateAncestorDirectories(root, rootSafeCandidateEntries.map(({ path: filePath }) => filePath));
14818
+ const candidateFiles = Array.from(new Set(gitCandidates.files.map(normalizePath)));
14819
+ const metadataCandidates = candidateFiles.filter((file2) => PROJECT_FILE_DEFINITIONS.some((definition, definitionIndex) => definition.kind === "file" && matchesDefinition(path9.basename(file2), definitionIndex)));
14820
+ const fileMatches = filterGitIgnoredEntries(await filterRealPathsWithinRootEntries(metadataCandidates, realRoot, reportMetadataFileChecks));
14821
+ const rawCandidateDirectories = collectCandidateAncestorDirectories(root, candidateFiles);
14796
14822
  const candidateDirectoryKeys = new Set(rawCandidateDirectories.map(fileIdentityKey));
14797
14823
  const safeSymlinkDirectoryKeys = new Set(safeSymlinkDirectories.map(fileIdentityKey));
14798
14824
  const directoryMatches = [
@@ -19547,7 +19573,7 @@ async function prepareSourceInput(file2, opts) {
19547
19573
  const rawSource2 = opts?.source ?? await fsp14.readFile(file2, "utf8");
19548
19574
  return prepareSFCSourceInput(rawSource2, framework);
19549
19575
  }
19550
- const sup = supportForFile(file2, opts?.languageExtensions);
19576
+ const sup = opts?.support ?? supportForFile(file2, opts?.languageExtensions);
19551
19577
  if (!sup)
19552
19578
  throw new UnsupportedParserInputError(file2);
19553
19579
  const rawSource = opts?.source ?? await fsp14.readFile(file2, "utf8");
@@ -21827,6 +21853,243 @@ function extractJsTsDynamicSpecifiers(source, fromFile, projectRoot) {
21827
21853
  }
21828
21854
  return out;
21829
21855
  }
21856
+ function maskPythonString(source, mask, start) {
21857
+ const quote = source[start];
21858
+ const triple = source[start + 1] === quote && source[start + 2] === quote;
21859
+ const delimiterLength = triple ? 3 : 1;
21860
+ for (let offset = 0; offset < delimiterLength; offset += 1) {
21861
+ mask[start + offset] = 1;
21862
+ }
21863
+ let index = start + delimiterLength;
21864
+ while (index < source.length) {
21865
+ const ch = source[index];
21866
+ if (!triple && (ch === "\n" || ch === "\r"))
21867
+ return index;
21868
+ mask[index] = 1;
21869
+ if (ch === "\\") {
21870
+ const nextIndex = index + 1;
21871
+ if (nextIndex < source.length)
21872
+ mask[nextIndex] = 1;
21873
+ index += 2;
21874
+ continue;
21875
+ }
21876
+ if (ch === quote) {
21877
+ if (!triple)
21878
+ return index + 1;
21879
+ if (source[index + 1] === quote && source[index + 2] === quote) {
21880
+ mask[index + 1] = 1;
21881
+ mask[index + 2] = 1;
21882
+ return index + delimiterLength;
21883
+ }
21884
+ }
21885
+ index += 1;
21886
+ }
21887
+ return source.length;
21888
+ }
21889
+ function buildPythonNonCodeMask(source) {
21890
+ if (!source.includes("#") && !source.includes("'") && !source.includes('"'))
21891
+ return void 0;
21892
+ const mask = new Uint8Array(source.length);
21893
+ for (let index = 0; index < source.length; index += 1) {
21894
+ const ch = source[index];
21895
+ if (ch === "#") {
21896
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
21897
+ mask[index] = 1;
21898
+ index += 1;
21899
+ }
21900
+ index -= 1;
21901
+ continue;
21902
+ }
21903
+ if (ch === "'" || ch === '"') {
21904
+ index = maskPythonString(source, mask, index) - 1;
21905
+ }
21906
+ }
21907
+ return mask;
21908
+ }
21909
+ function decodePythonStringContent(content, prefix) {
21910
+ if (prefix.includes("r"))
21911
+ return content;
21912
+ let decoded = "";
21913
+ for (let index = 0; index < content.length; index += 1) {
21914
+ const ch = content[index];
21915
+ if (ch !== "\\") {
21916
+ decoded += ch;
21917
+ continue;
21918
+ }
21919
+ index += 1;
21920
+ const escape2 = content[index];
21921
+ if (escape2 === void 0)
21922
+ return null;
21923
+ const simpleEscape = PYTHON_SIMPLE_STRING_ESCAPES[escape2];
21924
+ if (simpleEscape !== void 0) {
21925
+ decoded += simpleEscape;
21926
+ continue;
21927
+ }
21928
+ if (escape2 === "\n")
21929
+ continue;
21930
+ if (escape2 === "\r") {
21931
+ if (content[index + 1] === "\n")
21932
+ index += 1;
21933
+ continue;
21934
+ }
21935
+ let hexLength = 0;
21936
+ if (escape2 === "x")
21937
+ hexLength = 2;
21938
+ else if (escape2 === "u")
21939
+ hexLength = 4;
21940
+ else if (escape2 === "U")
21941
+ hexLength = 8;
21942
+ if (hexLength) {
21943
+ const digits = content.slice(index + 1, index + 1 + hexLength);
21944
+ if (digits.length !== hexLength || !/^[0-9a-fA-F]+$/.test(digits))
21945
+ return null;
21946
+ const codePoint = Number.parseInt(digits, 16);
21947
+ if (codePoint > 1114111)
21948
+ return null;
21949
+ decoded += String.fromCodePoint(codePoint);
21950
+ index += hexLength;
21951
+ continue;
21952
+ }
21953
+ if (escape2 >= "0" && escape2 <= "7") {
21954
+ let end = index + 1;
21955
+ while (end < content.length && end - index < 3) {
21956
+ const digit = content[end];
21957
+ if (digit < "0" || digit > "7")
21958
+ break;
21959
+ end += 1;
21960
+ }
21961
+ decoded += String.fromCodePoint(Number.parseInt(content.slice(index, end), 8));
21962
+ index = end - 1;
21963
+ continue;
21964
+ }
21965
+ decoded += `\\${escape2}`;
21966
+ }
21967
+ return decoded;
21968
+ }
21969
+ function parsePythonStaticStringLiteral(source, start) {
21970
+ let quoteIndex = start;
21971
+ let prefixLength = 0;
21972
+ while (prefixLength < 2) {
21973
+ const prefixCharacter = source[quoteIndex];
21974
+ if (!prefixCharacter || !"rRuUfF".includes(prefixCharacter))
21975
+ break;
21976
+ quoteIndex += 1;
21977
+ prefixLength += 1;
21978
+ }
21979
+ const prefix = source.slice(start, quoteIndex).toLowerCase();
21980
+ if (!["", "r", "u", "f", "fr", "rf"].includes(prefix))
21981
+ return null;
21982
+ const quote = source[quoteIndex];
21983
+ if (quote !== "'" && quote !== '"')
21984
+ return null;
21985
+ const triple = source[quoteIndex + 1] === quote && source[quoteIndex + 2] === quote;
21986
+ const delimiterLength = triple ? 3 : 1;
21987
+ const contentStart = quoteIndex + delimiterLength;
21988
+ for (let index = contentStart; index < source.length; index += 1) {
21989
+ const ch = source[index];
21990
+ if (!triple && (ch === "\n" || ch === "\r"))
21991
+ return null;
21992
+ if (ch === "\\") {
21993
+ index += 1;
21994
+ continue;
21995
+ }
21996
+ if (ch !== quote)
21997
+ continue;
21998
+ if (triple && (source[index + 1] !== quote || source[index + 2] !== quote))
21999
+ continue;
22000
+ const value = decodePythonStringContent(source.slice(contentStart, index), prefix);
22001
+ if (value === null)
22002
+ return null;
22003
+ return { value, end: index + delimiterLength };
22004
+ }
22005
+ return null;
22006
+ }
22007
+ function skipPythonExpressionTrivia(source, start) {
22008
+ let index = start;
22009
+ while (index < source.length) {
22010
+ while (index < source.length && /\s/.test(source[index]))
22011
+ index += 1;
22012
+ if (source[index] !== "#")
22013
+ break;
22014
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r")
22015
+ index += 1;
22016
+ }
22017
+ return index;
22018
+ }
22019
+ function parsePythonStaticStringExpression(source, start) {
22020
+ let index = skipPythonExpressionTrivia(source, start);
22021
+ let value = "";
22022
+ let literalCount = 0;
22023
+ while (true) {
22024
+ const literal2 = parsePythonStaticStringLiteral(source, index);
22025
+ if (!literal2)
22026
+ break;
22027
+ value += literal2.value;
22028
+ literalCount += 1;
22029
+ index = skipPythonExpressionTrivia(source, literal2.end);
22030
+ }
22031
+ if (!literalCount || source[index] !== "," && source[index] !== ")")
22032
+ return null;
22033
+ return value;
22034
+ }
22035
+ function collectPythonDynamicImportAliases(source) {
22036
+ const importlibAliases = /* @__PURE__ */ new Set(["importlib"]);
22037
+ const importModuleAliases = /* @__PURE__ */ new Set();
22038
+ const cleaned = stripPythonCommentsAndStrings(source);
22039
+ for (const match of cleaned.matchAll(/^\s*import\s+([^\r\n;]+)/gmu)) {
22040
+ for (const rawClause of (match[1] ?? "").split(",")) {
22041
+ const clause = rawClause.trim();
22042
+ const parsed = PYTHON_IMPORTLIB_ALIAS_PATTERN.exec(clause);
22043
+ if (parsed)
22044
+ importlibAliases.add(parsed[1] ?? "importlib");
22045
+ }
22046
+ }
22047
+ for (const match of cleaned.matchAll(PYTHON_FROM_IMPORTLIB_PATTERN)) {
22048
+ const importList = match[1] ?? match[2] ?? "";
22049
+ for (const rawClause of importList.split(",")) {
22050
+ const clause = rawClause.trim();
22051
+ const parsed = PYTHON_IMPORT_MODULE_ALIAS_PATTERN.exec(clause);
22052
+ if (parsed)
22053
+ importModuleAliases.add(parsed[1] ?? "import_module");
22054
+ }
22055
+ }
22056
+ return { importlibAliases, importModuleAliases };
22057
+ }
22058
+ function extractPythonDynamicSpecifiers(source) {
22059
+ const out = [];
22060
+ try {
22061
+ const mask = buildPythonNonCodeMask(source);
22062
+ const { importlibAliases, importModuleAliases } = collectPythonDynamicImportAliases(source);
22063
+ const seen = /* @__PURE__ */ new Set();
22064
+ for (const match of source.matchAll(PYTHON_DYNAMIC_CALL_PREFIX_PATTERN)) {
22065
+ if (!matchStartsInCode(mask, match))
22066
+ continue;
22067
+ const receiver = match[1] ?? "";
22068
+ const member = match[2];
22069
+ const isBuiltinImport = !member && receiver === "__import__";
22070
+ const isImportlibCall = member === "import_module" && importlibAliases.has(receiver);
22071
+ const isImportedFunctionCall = !member && importModuleAliases.has(receiver);
22072
+ if (!isBuiltinImport && !isImportlibCall && !isImportedFunctionCall)
22073
+ continue;
22074
+ const argumentStart = (match.index ?? 0) + (match[0]?.length ?? 0);
22075
+ const spec = parsePythonStaticStringExpression(source, argumentStart);
22076
+ if (!spec || !PYTHON_DYNAMIC_MODULE_PATTERN.test(spec) || seen.has(spec))
22077
+ continue;
22078
+ if (isBuiltinImport && spec.startsWith("."))
22079
+ continue;
22080
+ seen.add(spec);
22081
+ out.push({ spec, resolved: "heuristic", confidence: 0.7 });
22082
+ }
22083
+ } catch {
22084
+ }
22085
+ return out;
22086
+ }
22087
+ function extractDynamicImportSpecifiers(languageId, source, fromFile, projectRoot) {
22088
+ const extractor = DYNAMIC_IMPORT_SPECIFIER_EXTRACTORS[languageId];
22089
+ if (!extractor)
22090
+ return [];
22091
+ return extractor(source, fromFile, projectRoot);
22092
+ }
21830
22093
  function extractPythonSpecifiers(source) {
21831
22094
  const out = [];
21832
22095
  try {
@@ -21841,7 +22104,7 @@ function extractPythonSpecifiers(source) {
21841
22104
  }
21842
22105
  return out;
21843
22106
  }
21844
- var PYTHON_DOTTED_NAME_SOURCE;
22107
+ var PYTHON_DOTTED_NAME_SOURCE, PYTHON_IMPORTLIB_ALIAS_PATTERN, PYTHON_IMPORT_MODULE_ALIAS_PATTERN, PYTHON_DYNAMIC_CALL_PREFIX_PATTERN, PYTHON_DYNAMIC_MODULE_PATTERN, PYTHON_FROM_IMPORTLIB_PATTERN, PYTHON_SIMPLE_STRING_ESCAPES, DYNAMIC_IMPORT_SPECIFIER_EXTRACTORS;
21845
22108
  var init_specifiers = __esm({
21846
22109
  "dist/util/specifiers.js"() {
21847
22110
  "use strict";
@@ -21849,6 +22112,28 @@ var init_specifiers = __esm({
21849
22112
  init_identifiers();
21850
22113
  init_paths();
21851
22114
  PYTHON_DOTTED_NAME_SOURCE = String.raw`${PYTHON_IDENTIFIER_SOURCE}(?:\.${PYTHON_IDENTIFIER_SOURCE})*`;
22115
+ PYTHON_IMPORTLIB_ALIAS_PATTERN = new RegExp(String.raw`^importlib(?:\s+as\s+(${PYTHON_IDENTIFIER_SOURCE}))?$`, "u");
22116
+ PYTHON_IMPORT_MODULE_ALIAS_PATTERN = new RegExp(String.raw`^import_module(?:\s+as\s+(${PYTHON_IDENTIFIER_SOURCE}))?$`, "u");
22117
+ PYTHON_DYNAMIC_CALL_PREFIX_PATTERN = new RegExp(String.raw`(?<![._\p{XID_Continue}])(${PYTHON_IDENTIFIER_SOURCE})(?:\s*\.\s*(${PYTHON_IDENTIFIER_SOURCE}))?\s*\(\s*(?:name\s*=\s*)?`, "gmu");
22118
+ PYTHON_DYNAMIC_MODULE_PATTERN = new RegExp(String.raw`^\.*${PYTHON_DOTTED_NAME_SOURCE}$`, "u");
22119
+ PYTHON_FROM_IMPORTLIB_PATTERN = /^\s*from\s+importlib\s+import\s+(?:\(([\s\S]*?)\)|([^\r\n;]+))/gmu;
22120
+ PYTHON_SIMPLE_STRING_ESCAPES = {
22121
+ "\\": "\\",
22122
+ "'": "'",
22123
+ '"': '"',
22124
+ a: "\x07",
22125
+ b: "\b",
22126
+ f: "\f",
22127
+ n: "\n",
22128
+ r: "\r",
22129
+ t: " ",
22130
+ v: "\v"
22131
+ };
22132
+ DYNAMIC_IMPORT_SPECIFIER_EXTRACTORS = {
22133
+ js: extractJsTsDynamicSpecifiers,
22134
+ ts: extractJsTsDynamicSpecifiers,
22135
+ python: extractPythonDynamicSpecifiers
22136
+ };
21852
22137
  }
21853
22138
  });
21854
22139
 
@@ -25267,7 +25552,7 @@ var init_extractFacts = __esm({
25267
25552
  import crypto2 from "node:crypto";
25268
25553
  import fsp16 from "node:fs/promises";
25269
25554
  function isSqlFile(filePath, languageExtensions) {
25270
- return supportForFile(filePath, languageExtensions)?.id === "sql";
25555
+ return supportForFileWithoutHeaderSample(filePath, languageExtensions)?.id === "sql";
25271
25556
  }
25272
25557
  function sqlCorpusSignature(sqlFiles2, fileSignatures) {
25273
25558
  if (!sqlFiles2.length || !fileSignatures)
@@ -25466,6 +25751,17 @@ var init_sourceGraph = __esm({
25466
25751
  });
25467
25752
 
25468
25753
  // dist/graph-edge-collector.js
25754
+ function appendMissingSpecifiers(target, incoming) {
25755
+ if (!incoming.length)
25756
+ return;
25757
+ const existing = new Set(target.map((entry) => entry.spec));
25758
+ for (const entry of incoming) {
25759
+ if (existing.has(entry.spec))
25760
+ continue;
25761
+ existing.add(entry.spec);
25762
+ target.push(entry);
25763
+ }
25764
+ }
25469
25765
  function deduplicateEdges(edges, rawIsIdentity = false) {
25470
25766
  const deduplicated = /* @__PURE__ */ new Map();
25471
25767
  for (const edge of edges) {
@@ -25505,7 +25801,7 @@ async function collectEdgesForFile(file2, projectRoot, workspaceConfig, opts) {
25505
25801
  const sigEntry = opts.fileSignature;
25506
25802
  const sig = sigEntry?.sig;
25507
25803
  const gitSig = sigEntry?.gitSig;
25508
- const sqlFile = supportForFile(normalizedFile, opts.languageExtensions)?.id === "sql";
25804
+ const sqlFile = supportForFileWithoutHeaderSample(normalizedFile, opts.languageExtensions)?.id === "sql";
25509
25805
  const emitCacheEntry = (edges2) => {
25510
25806
  if (!sig || !opts.onFileEdges)
25511
25807
  return;
@@ -25572,17 +25868,8 @@ async function collectEdgesForFile(file2, projectRoot, workspaceConfig, opts) {
25572
25868
  ...opts.native ? { native: opts.native } : {},
25573
25869
  ...opts.logLevel ? { logLevel: opts.logLevel } : {}
25574
25870
  });
25575
- if ((sup.id === "ts" || sup.id === "js") && opts.dynamicImportHeuristics) {
25576
- const dynamicSpecs = extractJsTsDynamicSpecifiers(src, normalizedFile, projectRoot);
25577
- if (dynamicSpecs.length) {
25578
- const existing = new Set(specs.map((entry) => entry.spec));
25579
- for (const entry of dynamicSpecs) {
25580
- if (existing.has(entry.spec))
25581
- continue;
25582
- existing.add(entry.spec);
25583
- specs.push(entry);
25584
- }
25585
- }
25871
+ if (opts.dynamicImportHeuristics) {
25872
+ appendMissingSpecifiers(specs, extractDynamicImportSpecifiers(sup.id, src, normalizedFile, projectRoot));
25586
25873
  }
25587
25874
  const specSources = specs.map((entry) => ({ entry, support: sup }));
25588
25875
  for (const block of embeddedBlocks) {
@@ -25594,6 +25881,9 @@ async function collectEdgesForFile(file2, projectRoot, workspaceConfig, opts) {
25594
25881
  ...opts.native ? { native: opts.native } : {},
25595
25882
  ...opts.logLevel ? { logLevel: opts.logLevel } : {}
25596
25883
  });
25884
+ if (opts.dynamicImportHeuristics) {
25885
+ appendMissingSpecifiers(blockSpecs, extractDynamicImportSpecifiers(block.sup.id, block.source, normalizedFile, projectRoot));
25886
+ }
25597
25887
  for (const entry of blockSpecs) {
25598
25888
  specSources.push({ entry, support: block.sup });
25599
25889
  }
@@ -25672,7 +25962,7 @@ function canReuseSqlEdgeCache(file2, sqlCorpusSig, fileSignatures, cachedFileEdg
25672
25962
  async function collectGraph(projectRoot, files, opts) {
25673
25963
  const normalizedFiles = files.map(normalizePath);
25674
25964
  const normalizedAllFiles = (opts?.allFiles ?? files).map(normalizePath);
25675
- const isSqlFile4 = (file2) => supportForFile(file2, opts?.languageExtensions)?.id === "sql";
25965
+ const isSqlFile4 = (file2) => supportForFileWithoutHeaderSample(file2, opts?.languageExtensions)?.id === "sql";
25676
25966
  const hasExplicitReplace = !!opts?.replaceFiles;
25677
25967
  const requestedReplaceSet = hasExplicitReplace ? new Set(Array.from(opts.replaceFiles ?? [], (file2) => normalizePath(file2))) : new Set(normalizedFiles);
25678
25968
  const sqlFiles2 = Array.from(new Set(normalizedAllFiles.filter(isSqlFile4))).sort((left, right) => left.localeCompare(right));
@@ -26336,8 +26626,12 @@ function parsePreparedFileContext(context) {
26336
26626
  }
26337
26627
  throw new Error(`Failed to reconstruct syntax tree for ${context.file}`);
26338
26628
  }
26339
- async function prepareFileForIndexing(file2, native, languageExtensions, source) {
26340
- const prep = await prepareSourceInput(file2, { languageExtensions, ...source !== void 0 ? { source } : {} });
26629
+ async function prepareFileForIndexing(file2, native, languageExtensions, source, support) {
26630
+ const prep = await prepareSourceInput(file2, {
26631
+ languageExtensions,
26632
+ ...source !== void 0 ? { source } : {},
26633
+ ...support ? { support } : {}
26634
+ });
26341
26635
  if (isGraphOnlyLanguage(prep.sup.id)) {
26342
26636
  return {
26343
26637
  file: file2,
@@ -32499,7 +32793,7 @@ async function cacheSignatureForFile(file2, sigInfo, opts) {
32499
32793
  async function buildBloomFilterForFile(file2, opts) {
32500
32794
  try {
32501
32795
  const source = await fsp18.readFile(file2, "utf8");
32502
- const support = supportForFile(file2, opts?.languageExtensions);
32796
+ const support = supportForFileWithoutHeaderSample(file2, opts?.languageExtensions);
32503
32797
  if (!support)
32504
32798
  return null;
32505
32799
  return buildBloomFilterFromSource(source, support);
@@ -33267,8 +33561,13 @@ function winnowShingles(shingles, windowSize, maxFingerprints) {
33267
33561
  }
33268
33562
  return fingerprints;
33269
33563
  }
33270
- function languageForFile(filePath) {
33271
- const support = supportForFile(filePath);
33564
+ function isDuplicateCandidateFile(filePath) {
33565
+ if (supportForFileWithoutHeaderSample(filePath))
33566
+ return true;
33567
+ return !!textLanguageByExtension[path39.extname(filePath).toLowerCase()];
33568
+ }
33569
+ function languageForFile(filePath, source) {
33570
+ const support = supportForFileWithSource(filePath, source);
33272
33571
  if (support) {
33273
33572
  return { id: support.id, textOnly: false };
33274
33573
  }
@@ -33443,7 +33742,7 @@ async function getDuplicateAstContext(index, file2, source, cache) {
33443
33742
  return context;
33444
33743
  }
33445
33744
  try {
33446
- const prepared = await prepareSourceInput(file2, { source });
33745
+ const prepared = await prepareSourceInput(file2, { source, support: supportForFileWithSource(file2, source) });
33447
33746
  const attempt = attemptParsePreparedFileContext({
33448
33747
  file: file2,
33449
33748
  source: prepared.source,
@@ -33610,11 +33909,11 @@ async function collectDuplicateUnits(index, options) {
33610
33909
  return { units, belowThresholdUnits, belowThresholdUnitsByFile };
33611
33910
  }
33612
33911
  async function buildDuplicateUnitsForFile(index, file2, projectRoot, minTokens, maxTokens, shingleSize, windowSize, astContextCache) {
33613
- const moduleIndex = index.byFile.get(fileIdentityKey(file2));
33614
- const language = languageForFile(file2);
33615
- if (!language)
33912
+ const fileKey = fileIdentityKey(file2);
33913
+ const moduleIndex = index.byFile.get(fileKey);
33914
+ if (!isDuplicateCandidateFile(file2))
33616
33915
  return [];
33617
- let source = index.parsed?.get(fileIdentityKey(file2))?.source;
33916
+ let source = index.parsed?.get(fileKey)?.source;
33618
33917
  if (source === void 0) {
33619
33918
  try {
33620
33919
  source = await fsp19.readFile(file2, "utf8");
@@ -33622,6 +33921,9 @@ async function buildDuplicateUnitsForFile(index, file2, projectRoot, minTokens,
33622
33921
  return [];
33623
33922
  }
33624
33923
  }
33924
+ const language = languageForFile(file2, source);
33925
+ if (!language)
33926
+ return [];
33625
33927
  const astContext = language.textOnly ? void 0 : await getDuplicateAstContext(index, file2, source, astContextCache);
33626
33928
  const { chunks, symbolChunks } = makeDuplicateChunksWithSymbols(file2, language.id, language.textOnly, source, minTokens, maxTokens);
33627
33929
  const symbolUnits = (moduleIndex?.locals ?? []).map((symbol2) => {
@@ -51174,7 +51476,7 @@ function moduleNameLookup(index, file2) {
51174
51476
  if (!lookups) {
51175
51477
  lookups = /* @__PURE__ */ new Map();
51176
51478
  for (const moduleEntry of index.byFile.values()) {
51177
- const normalizeIdentifier = supportForFile(moduleEntry.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
51479
+ const normalizeIdentifier = supportForFileWithoutHeaderSample(moduleEntry.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
51178
51480
  const localExports = /* @__PURE__ */ new Map();
51179
51481
  const namespaceReexports = /* @__PURE__ */ new Map();
51180
51482
  const reexports = /* @__PURE__ */ new Map();
@@ -51219,7 +51521,7 @@ function sameSymbolDef(index, left, right) {
51219
51521
  if (fileIdentityKey(left.file) !== fileIdentityKey(right.file) || left.kind !== right.kind) {
51220
51522
  return false;
51221
51523
  }
51222
- const normalizeIdentifier = supportForFile(left.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
51524
+ const normalizeIdentifier = supportForFileWithoutHeaderSample(left.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
51223
51525
  if (normalizeIdentifier(left.localName) !== normalizeIdentifier(right.localName)) {
51224
51526
  return false;
51225
51527
  }
@@ -51249,64 +51551,68 @@ function packageNameCacheFor(index) {
51249
51551
  }
51250
51552
  return caches;
51251
51553
  }
51554
+ function packageDeclarationSource(index, filePath, fileKey) {
51555
+ const retained = index.parsed?.get(fileKey)?.source;
51556
+ if (retained !== void 0)
51557
+ return retained;
51558
+ try {
51559
+ return fs20.readFileSync(filePath, "utf8");
51560
+ } catch {
51561
+ return null;
51562
+ }
51563
+ }
51252
51564
  function readGoPackageName(index, filePath) {
51253
51565
  const cache = packageNameCacheFor(index).go;
51254
51566
  const key = fileIdentityKey(filePath);
51255
51567
  const cached2 = cache.get(key);
51256
51568
  if (cached2 !== void 0)
51257
51569
  return cached2;
51258
- try {
51259
- const source = fs20.readFileSync(filePath, "utf8");
51260
- const match = GO_PACKAGE_PATTERN.exec(source);
51261
- const packageName = match?.[1] ?? null;
51262
- cache.set(key, packageName);
51263
- return packageName;
51264
- } catch {
51570
+ const source = packageDeclarationSource(index, filePath, key);
51571
+ if (source === null) {
51265
51572
  cache.set(key, null);
51266
51573
  return null;
51267
51574
  }
51575
+ const packageName = GO_PACKAGE_PATTERN.exec(source)?.[1] ?? null;
51576
+ cache.set(key, packageName);
51577
+ return packageName;
51268
51578
  }
51269
51579
  function resolveGoPackageExport(index, file2, exportedName) {
51270
- try {
51271
- const support = supportForFile(file2, index.languageExtensions);
51272
- if (!support || support.id !== "go")
51273
- return null;
51274
- const directory = packageDirectoryLookup(index, "go").get(fileIdentityKey(path42.dirname(file2)));
51275
- if (!directory)
51276
- return null;
51277
- const sourcePackage = readGoPackageName(index, file2);
51278
- const candidates = sourcePackage ? directory.byName.get(sourcePackage) ?? [] : directory.all;
51279
- const matches = [];
51280
- for (const moduleEntry of candidates) {
51281
- const names = moduleNameLookup(index, moduleEntry.file);
51282
- if (!names)
51283
- continue;
51284
- for (const target of names.localExports.get(names.normalizeIdentifier(exportedName)) ?? []) {
51285
- if (!matches.some((candidate) => sameSymbolDef(index, candidate, target))) {
51286
- matches.push(target);
51287
- }
51580
+ if (supportForFileWithoutHeaderSample(file2, index.languageExtensions)?.id !== "go")
51581
+ return null;
51582
+ const directory = packageDirectoryLookup(index, "go").get(fileIdentityKey(path42.dirname(file2)));
51583
+ if (!directory)
51584
+ return null;
51585
+ const sourcePackage = readGoPackageName(index, file2);
51586
+ const candidates = sourcePackage ? directory.byName.get(sourcePackage) ?? [] : directory.all;
51587
+ const matches = [];
51588
+ for (const moduleEntry of candidates) {
51589
+ const names = moduleNameLookup(index, moduleEntry.file);
51590
+ if (!names)
51591
+ continue;
51592
+ for (const target of names.localExports.get(names.normalizeIdentifier(exportedName)) ?? []) {
51593
+ if (!matches.some((candidate) => sameSymbolDef(index, candidate, target))) {
51594
+ matches.push(target);
51288
51595
  }
51289
51596
  }
51290
- return matches.length === 1 ? matches[0] ?? null : null;
51291
- } catch {
51292
51597
  }
51293
- return null;
51598
+ return matches.length === 1 ? matches[0] ?? null : null;
51294
51599
  }
51295
51600
  function readPackageNameForLanguage(index, filePath, languageId) {
51296
51601
  const cache = packageNameCacheFor(index).jvm;
51297
- const key = `${languageId}::${fileIdentityKey(filePath)}`;
51602
+ const fileKey = fileIdentityKey(filePath);
51603
+ const key = `${languageId}::${fileKey}`;
51298
51604
  const cached2 = cache.get(key);
51299
51605
  if (cached2 !== void 0)
51300
51606
  return cached2;
51301
- try {
51302
- const source = fs20.readFileSync(filePath, "utf8");
51303
- const packageName = languageId === "kotlin" ? KOTLIN_PACKAGE_NAME_PATTERN.exec(source)?.[1] ?? null : JAVA_PACKAGE_NAME_PATTERN.exec(source)?.[1] ?? null;
51304
- cache.set(key, packageName);
51305
- return packageName;
51306
- } catch {
51607
+ const source = packageDeclarationSource(index, filePath, fileKey);
51608
+ if (source === null) {
51307
51609
  cache.set(key, null);
51308
51610
  return null;
51309
51611
  }
51612
+ const pattern = languageId === "kotlin" ? KOTLIN_PACKAGE_NAME_PATTERN : JAVA_PACKAGE_NAME_PATTERN;
51613
+ const packageName = pattern.exec(source)?.[1] ?? null;
51614
+ cache.set(key, packageName);
51615
+ return packageName;
51310
51616
  }
51311
51617
  function packageDirectoryLookup(index, languageId) {
51312
51618
  let byLanguage = packageDirectoryLookups.get(index);
@@ -51326,6 +51632,9 @@ function packageDirectoryLookup(index, languageId) {
51326
51632
  directories.set(directoryKey, directory);
51327
51633
  }
51328
51634
  directory.all.push(moduleEntry);
51635
+ const moduleLanguageId = supportForFileWithoutHeaderSample(moduleEntry.file, index.languageExtensions)?.id;
51636
+ if (!moduleLanguageId || !PACKAGE_DECLARING_LANGUAGE_IDS[languageId].has(moduleLanguageId))
51637
+ continue;
51329
51638
  const packageName = languageId === "go" ? readGoPackageName(index, moduleEntry.file) : readPackageNameForLanguage(index, moduleEntry.file, languageId);
51330
51639
  if (!packageName)
51331
51640
  continue;
@@ -51552,30 +51861,23 @@ function resolveImported(index, imp, exportedName) {
51552
51861
  return hit.def;
51553
51862
  if (hit?.kind === "namespace")
51554
51863
  return { namespace: hit.file };
51555
- try {
51556
- const support = supportForFile(targetFile);
51557
- if (support?.id === "java" || support?.id === "kotlin") {
51558
- const siblingHit = resolveSiblingPackageExport(index, targetFile, exportedName, support.id);
51559
- if (siblingHit?.kind === "resolved")
51560
- return siblingHit.def;
51561
- if (siblingHit?.kind === "namespace") {
51562
- return { namespace: siblingHit.file };
51563
- }
51864
+ const support = supportForFileWithoutHeaderSample(targetFile, index.languageExtensions);
51865
+ if (support?.id === "java" || support?.id === "kotlin") {
51866
+ const siblingHit = resolveSiblingPackageExport(index, targetFile, exportedName, support.id);
51867
+ if (siblingHit?.kind === "resolved")
51868
+ return siblingHit.def;
51869
+ if (siblingHit?.kind === "namespace") {
51870
+ return { namespace: siblingHit.file };
51564
51871
  }
51565
- } catch {
51566
51872
  }
51567
- try {
51568
- const support = supportForFile(targetFile);
51569
- if (support?.id === "python") {
51570
- const submodule = resolvePythonSubmodule(targetFile, exportedName);
51571
- if (submodule)
51572
- return { namespace: submodule };
51573
- }
51574
- } catch {
51873
+ if (support?.id === "python") {
51874
+ const submodule = resolvePythonSubmodule(targetFile, exportedName);
51875
+ if (submodule)
51876
+ return { namespace: submodule };
51575
51877
  }
51576
51878
  return null;
51577
51879
  }
51578
- var GO_PACKAGE_PATTERN, JAVA_PACKAGE_NAME_PATTERN, KOTLIN_PACKAGE_NAME_PATTERN, moduleNameLookups, packageDirectoryLookups, packageNameCaches;
51880
+ var GO_PACKAGE_PATTERN, JAVA_PACKAGE_NAME_PATTERN, KOTLIN_PACKAGE_NAME_PATTERN, PACKAGE_DECLARING_LANGUAGE_IDS, moduleNameLookups, packageDirectoryLookups, packageNameCaches;
51579
51881
  var init_navigation_resolve = __esm({
51580
51882
  "dist/indexer/navigation-resolve.js"() {
51581
51883
  "use strict";
@@ -51586,6 +51888,11 @@ var init_navigation_resolve = __esm({
51586
51888
  GO_PACKAGE_PATTERN = new RegExp(String.raw`^\s*package\s+(${GO_IDENTIFIER_SOURCE})`, "mu");
51587
51889
  JAVA_PACKAGE_NAME_PATTERN = new RegExp(String.raw`^\s*package\s+(${JAVA_IDENTIFIER_SOURCE}(?:\.${JAVA_IDENTIFIER_SOURCE})*)\s*;`, "mu");
51588
51890
  KOTLIN_PACKAGE_NAME_PATTERN = new RegExp(String.raw`^\s*package\s+(${KOTLIN_IDENTIFIER_SOURCE}(?:\.${KOTLIN_IDENTIFIER_SOURCE})*)`, "mu");
51891
+ PACKAGE_DECLARING_LANGUAGE_IDS = {
51892
+ go: /* @__PURE__ */ new Set(["go"]),
51893
+ java: /* @__PURE__ */ new Set(["java", "kotlin"]),
51894
+ kotlin: /* @__PURE__ */ new Set(["java", "kotlin"])
51895
+ };
51589
51896
  moduleNameLookups = /* @__PURE__ */ new WeakMap();
51590
51897
  packageDirectoryLookups = /* @__PURE__ */ new WeakMap();
51591
51898
  packageNameCaches = /* @__PURE__ */ new WeakMap();
@@ -54643,7 +54950,7 @@ function isNativeWorkerEligibleFile(filePath, support) {
54643
54950
  function countNativeWorkerEligibleFiles(files, languageExtensions) {
54644
54951
  let count = 0;
54645
54952
  for (const file2 of files) {
54646
- if (isNativeWorkerEligibleFile(file2, supportForFile(file2, languageExtensions)))
54953
+ if (isNativeWorkerEligibleFile(file2, supportForFileWithoutHeaderSample(file2, languageExtensions)))
54647
54954
  count++;
54648
54955
  }
54649
54956
  return count;
@@ -54778,10 +55085,10 @@ async function prepareFileContextForBuild(file2, support, opts, workerSetup, rep
54778
55085
  workerSetup.report.errors.push({ file: file2, message: stringifyUnknown(error51) });
54779
55086
  }
54780
55087
  }
54781
- prepared = await prepareFileForIndexing(file2, opts?.native, opts?.languageExtensions, source);
55088
+ prepared = await prepareFileForIndexing(file2, opts?.native, opts?.languageExtensions, source, support);
54782
55089
  }
54783
55090
  } else {
54784
- prepared = await prepareFileForIndexing(file2, opts?.native, opts?.languageExtensions, source);
55091
+ prepared = await prepareFileForIndexing(file2, opts?.native, opts?.languageExtensions, source, support);
54785
55092
  }
54786
55093
  recordPreparedNativeExecutionOutcome(report, prepared);
54787
55094
  return prepared;
@@ -55425,7 +55732,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55425
55732
  const sqlFiles2 = normalizedFiles.filter((file2) => {
55426
55733
  if (!languageExtensions)
55427
55734
  return path47.extname(file2).toLowerCase() === ".sql";
55428
- return supportForFile(file2, languageExtensions)?.id === "sql";
55735
+ return supportForFileWithoutHeaderSample(file2, languageExtensions)?.id === "sql";
55429
55736
  }).sort((left, right) => left.localeCompare(right));
55430
55737
  const fileSignatures = await prepareFileSignatures({
55431
55738
  projectRoot,
@@ -55443,7 +55750,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55443
55750
  return sqlFactCachePromise;
55444
55751
  };
55445
55752
  const shouldProvideSqlFactCache = (file2, sigInfo, cachedEdgesEntry) => {
55446
- if (supportForFile(file2, opts?.languageExtensions)?.id !== "sql")
55753
+ if (supportForFileWithoutHeaderSample(file2, opts?.languageExtensions)?.id !== "sql")
55447
55754
  return false;
55448
55755
  if (!cachedEdgesEntry || !sqlCorpusSig || cachedEdgesEntry.sqlCorpusSig !== sqlCorpusSig)
55449
55756
  return true;
@@ -55726,7 +56033,8 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55726
56033
  await writeProjectIndexSnapshot(projectRoot, opts, index, projectSnapshotFilesSignature(manifestEntries, projectRoot));
55727
56034
  }
55728
56035
  if (buildStartedAt !== void 0) {
55729
- emitIndexLifecycleProgress(opts, "complete", "build", index.byFile.size, performance5.now() - buildStartedAt);
56036
+ const progressStartedAt = opts?.progressStartedAt ?? buildStartedAt;
56037
+ emitIndexLifecycleProgress(opts, "complete", "build", index.byFile.size, performance5.now() - progressStartedAt);
55730
56038
  }
55731
56039
  return index;
55732
56040
  } finally {
@@ -55801,9 +56109,13 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
55801
56109
  }
55802
56110
  }
55803
56111
  async function buildProjectIndex(projectRoot, opts) {
55804
- return buildProjectIndexWithManifestOptions(projectRoot, opts);
56112
+ return buildProjectIndexWithManifestOptions(projectRoot, {
56113
+ ...opts,
56114
+ progressStartedAt: opts?.progressStartedAt ?? performance5.now()
56115
+ });
55805
56116
  }
55806
- async function buildProjectIndexFromFiles(projectRoot, inputFiles, opts) {
56117
+ async function buildProjectIndexFromFiles(projectRoot, inputFiles, rawOpts) {
56118
+ const opts = { ...rawOpts, progressStartedAt: rawOpts?.progressStartedAt ?? performance5.now() };
55807
56119
  try {
55808
56120
  const useDiskCache = (opts?.cache ?? "off") === "disk";
55809
56121
  const normalizedInputFiles = await normalizeIndexedFileInputsWithinRoot(projectRoot, inputFiles, "Index file");
@@ -55832,7 +56144,11 @@ async function buildDeclaredScopeIndex(projectRoot, scopeFiles, opts) {
55832
56144
  }
55833
56145
  }
55834
56146
  }
55835
- async function buildProjectIndexIncremental(projectRoot, opts) {
56147
+ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56148
+ const opts = {
56149
+ ...rawOpts,
56150
+ progressStartedAt: rawOpts?.progressStartedAt ?? performance5.now()
56151
+ };
55836
56152
  await initializeFileIdentityCaseSensitivity(projectRoot);
55837
56153
  clearResolutionCaches();
55838
56154
  const graphOptions = normalizeGraphOptions(opts?.graph);
@@ -58037,7 +58353,24 @@ function emitAgentFilePlanProgress(options) {
58037
58353
  total: 0
58038
58354
  });
58039
58355
  }
58356
+ function agentDiscoveryProgressReporter(options) {
58357
+ const onProgress = options.buildOptions?.onProgress;
58358
+ if (!onProgress)
58359
+ return void 0;
58360
+ return ({ activity, current, total }) => {
58361
+ onProgress({
58362
+ type: "progress",
58363
+ phase: "update",
58364
+ mode: "check",
58365
+ message: activity,
58366
+ activity,
58367
+ current,
58368
+ total
58369
+ });
58370
+ };
58371
+ }
58040
58372
  async function resolveAgentSessionFilePlan(options) {
58373
+ const startedAt = performance.now();
58041
58374
  const { discoveryOptions, graphOptions, languageExtensions, cacheLocation } = await resolveAgentDiscoverySettings(options);
58042
58375
  const incrementalOptions = {
58043
58376
  ...options.buildOptions,
@@ -58051,6 +58384,7 @@ async function resolveAgentSessionFilePlan(options) {
58051
58384
  return {
58052
58385
  files: incrementalPlan.files,
58053
58386
  incrementalPlan,
58387
+ startedAt,
58054
58388
  ...discoveryOptions ? { discoveryOptions } : {},
58055
58389
  ...graphOptions ? { graphOptions } : {},
58056
58390
  ...languageExtensions ? { languageExtensions } : {},
@@ -58061,9 +58395,14 @@ async function resolveAgentSessionFilePlan(options) {
58061
58395
  const customPatterns = languageExtensionPatterns(languageExtensions);
58062
58396
  const patterns = customPatterns.length ? [...DEFAULT_PROJECT_PATTERNS2, ...customPatterns] : void 0;
58063
58397
  emitAgentFilePlanProgress(options);
58064
- const files = await listProjectFiles(options.root, patterns, discoveryOptions);
58398
+ const onDiscoveryProgress = agentDiscoveryProgressReporter(options);
58399
+ const files = await listProjectFiles(options.root, patterns, {
58400
+ ...discoveryOptions,
58401
+ ...onDiscoveryProgress ? { onDiscoveryProgress } : {}
58402
+ });
58065
58403
  return {
58066
58404
  files,
58405
+ startedAt,
58067
58406
  ...discoveryOptions ? { discoveryOptions } : {},
58068
58407
  ...graphOptions ? { graphOptions } : {},
58069
58408
  ...languageExtensions ? { languageExtensions } : {},
@@ -58216,7 +58555,7 @@ function createAgentSession(options) {
58216
58555
  if (cachedBase)
58217
58556
  return cachedBase;
58218
58557
  const loadPromise = (async () => {
58219
- const { files, discoveryOptions, graphOptions, languageExtensions, cacheLocation, incrementalPlan } = await loadFilePlan();
58558
+ const { files, discoveryOptions, graphOptions, languageExtensions, cacheLocation, incrementalPlan, startedAt } = await loadFilePlan();
58220
58559
  const buildOptions = {
58221
58560
  ...options.buildOptions,
58222
58561
  ...graphOptions ? { graph: graphOptions } : {},
@@ -58224,6 +58563,8 @@ function createAgentSession(options) {
58224
58563
  keepParsed: options.buildOptions?.keepParsed ?? true,
58225
58564
  files,
58226
58565
  filesAreProjectScope: true,
58566
+ // Discovery ran before this build, so the completion event must measure from there.
58567
+ progressStartedAt: options.buildOptions?.progressStartedAt ?? startedAt,
58227
58568
  ...incrementalPlan ? {
58228
58569
  reconciledManifestUpdatedAt: incrementalPlan.manifestUpdatedAt,
58229
58570
  reconciledWorkingTreeDiffFiles: incrementalPlan.workingTreeDiffFiles,
@@ -64194,7 +64535,7 @@ async function checkMarkdownLinksInFiles(projectRoot, files) {
64194
64535
  return absoluteFile;
64195
64536
  }
64196
64537
  }));
64197
- const markdownFiles = Array.from(new Set(markdownFileCandidates)).filter((file2) => isFilePathWithinRoot(realRoot, file2)).filter((file2) => supportForFile(file2)?.id === "markdown");
64538
+ const markdownFiles = Array.from(new Set(markdownFileCandidates)).filter((file2) => isFilePathWithinRoot(realRoot, file2)).filter((file2) => supportForFileWithoutHeaderSample(file2)?.id === "markdown");
64198
64539
  const targetStatusByPath = /* @__PURE__ */ new Map();
64199
64540
  const anchorsByPath = /* @__PURE__ */ new Map();
64200
64541
  const failures = [];
@@ -64233,7 +64574,7 @@ async function checkMarkdownLinksInFiles(projectRoot, files) {
64233
64574
  failures.push(toFailure(realRoot, displayFile, occurrence, "outside_root", resolvedTarget));
64234
64575
  continue;
64235
64576
  }
64236
- if (!target.fragment || targetStatus.isDirectory || supportForFile(resolvedTarget)?.id !== "markdown")
64577
+ if (!target.fragment || targetStatus.isDirectory || supportForFileWithoutHeaderSample(resolvedTarget)?.id !== "markdown")
64237
64578
  continue;
64238
64579
  const anchors = await cachedMarkdownAnchors(targetStatus.realPath, anchorsByPath);
64239
64580
  if (!anchors.has(target.fragment)) {
@@ -65302,7 +65643,7 @@ var init_navigation_php = __esm({
65302
65643
  function sameDef(left, right, languageExtensions) {
65303
65644
  const leftIndex = left.range.start.index ?? 0;
65304
65645
  const rightIndex = right.range.start.index ?? 0;
65305
- const normalizeIdentifier = supportForFile(left.file, languageExtensions)?.normalizeIdentifier ?? ((name) => name);
65646
+ const normalizeIdentifier = supportForFileWithoutHeaderSample(left.file, languageExtensions)?.normalizeIdentifier ?? ((name) => name);
65306
65647
  return fileIdentityKey(left.file) === fileIdentityKey(right.file) && normalizeIdentifier(left.localName) === normalizeIdentifier(right.localName) && leftIndex === rightIndex;
65307
65648
  }
65308
65649
  function rangeContains(range, pos) {
@@ -65585,7 +65926,7 @@ function hasExpandedNamedImport(moduleIndex, targetFile, symbolName) {
65585
65926
  return moduleIndex.imports.some((candidate) => candidate.kind === "named" && candidate.local === symbolName && candidate.imported === symbolName && typeof candidate.resolved === "string" && fileIdentityKey(candidate.resolved) === targetKey);
65586
65927
  }
65587
65928
  function referenceCandidateCacheKey(index, def, exportedNames) {
65588
- const normalizeIdentifier = supportForFile(def.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
65929
+ const normalizeIdentifier = supportForFileWithoutHeaderSample(def.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
65589
65930
  const sortedNames = exportedNames.map(normalizeIdentifier).sort();
65590
65931
  return `${fileIdentityKey(def.file)}::${def.range.start.index ?? 0}::canonical::${sortedNames.join("\0")}`;
65591
65932
  }
@@ -66363,7 +66704,7 @@ async function findReferences(index, req, opts) {
66363
66704
  const filter = index.bloomFilters?.get(fileIdentityKey(candidateFile));
66364
66705
  if (!filter)
66365
66706
  return true;
66366
- const normalizeIdentifier = supportForFile(candidateFile, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
66707
+ const normalizeIdentifier = supportForFileWithoutHeaderSample(candidateFile, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
66367
66708
  const aliases = getCandidateReferenceNames(module2, definitionFile, exportedNameSet);
66368
66709
  if (!aliases.length) {
66369
66710
  return [...exportedNames, ...phpQualifiedNames].some((candidateName) => filter.mightContain(normalizeIdentifier(candidateName)));
@@ -66393,7 +66734,7 @@ async function findReferences(index, req, opts) {
66393
66734
  }
66394
66735
  return scopeIndex;
66395
66736
  };
66396
- if (supportForFile(fileId, index.languageExtensions)?.supportsExportFromReferences) {
66737
+ if (supportForFileWithoutHeaderSample(fileId, index.languageExtensions)?.supportsExportFromReferences) {
66397
66738
  for (const entry of module2.exports) {
66398
66739
  if (hasReachedMaxReferences())
66399
66740
  break;
@@ -66521,7 +66862,7 @@ async function findReferences(index, req, opts) {
66521
66862
  if (hasReachedMaxReferences())
66522
66863
  break;
66523
66864
  const filter = index.bloomFilters?.get(fileIdentityKey(fileId));
66524
- const canonicalName = supportForFile(fileId, index.languageExtensions)?.normalizeIdentifier(def.localName) ?? def.localName;
66865
+ const canonicalName = supportForFileWithoutHeaderSample(fileId, index.languageExtensions)?.normalizeIdentifier(def.localName) ?? def.localName;
66525
66866
  if (filter && !filter.mightContain(canonicalName))
66526
66867
  continue;
66527
66868
  const remainingReferences = maxReferences !== void 0 ? Math.max(0, maxReferences - refs.length) : void 0;
@@ -68023,7 +68364,7 @@ function hasDiagnostics(diagnostics) {
68023
68364
  return !!(diagnostics.missingFiles.length || diagnostics.symbolMappingParseFailures.length || diagnostics.memberResolutionCoverage?.limitedLanguages.length);
68024
68365
  }
68025
68366
  function isRiskRelevantSymbolMappingFile(file2) {
68026
- return supportForFile(file2)?.supportsCrossModuleSymbols ?? false;
68367
+ return supportForFileWithoutHeaderSample(file2)?.supportsCrossModuleSymbols ?? false;
68027
68368
  }
68028
68369
  var init_risk = __esm({
68029
68370
  "dist/review/risk.js"() {
@@ -69919,7 +70260,7 @@ async function collectVerifiedCallsiteReferences(index, changedSymbol, maxRefs,
69919
70260
  if (!shouldIncludeReference(file2)) {
69920
70261
  continue;
69921
70262
  }
69922
- const support = supportForFile(file2);
70263
+ const support = supportForFileWithoutHeaderSample(file2, index.languageExtensions);
69923
70264
  if (!support || !supportsCallCompatibilityLanguage(support.id)) {
69924
70265
  continue;
69925
70266
  }
@@ -70332,7 +70673,7 @@ var init_hunks = __esm({
70332
70673
  // dist/impact/map.js
70333
70674
  async function locateChangedSymbolsWithLines(index, file2, hunks) {
70334
70675
  const changedLines = collectChangedLines(hunks);
70335
- if (isGraphOnlyFile(file2)) {
70676
+ if (isGraphOnlyFile(file2, index.languageExtensions)) {
70336
70677
  return { changedSymbols: [], changedLines, parseFailed: false };
70337
70678
  }
70338
70679
  let parsedEntry;
@@ -70472,7 +70813,7 @@ function sameRangeStartPosition(left, right) {
70472
70813
  return left.start.line === right.start.line && left.start.column === right.start.column && left.start.index === right.start.index;
70473
70814
  }
70474
70815
  async function mapChangedLinesToSymbols(index, file2, hunks, changedLinesOverride) {
70475
- if (isGraphOnlyFile(file2)) {
70816
+ if (isGraphOnlyFile(file2, index.languageExtensions)) {
70476
70817
  return /* @__PURE__ */ new Map();
70477
70818
  }
70478
70819
  let parsedEntry;
@@ -70513,8 +70854,8 @@ async function mapChangedLinesToSymbols(index, file2, hunks, changedLinesOverrid
70513
70854
  }
70514
70855
  return linesByHandle;
70515
70856
  }
70516
- function isGraphOnlyFile(file2) {
70517
- const support = supportForFile(file2);
70857
+ function isGraphOnlyFile(file2, languageExtensions) {
70858
+ const support = supportForFileWithoutHeaderSample(file2, languageExtensions);
70518
70859
  return support ? isGraphOnlyLanguage(support.id) : false;
70519
70860
  }
70520
70861
  function hasOverlapSorted(sorted, lo, hi) {
@@ -73054,17 +73395,25 @@ function isNodeBuiltinSpecifier(specifier) {
73054
73395
  return NODE_BUILTIN_MODULES2.has(specifier);
73055
73396
  }
73056
73397
  function isGraphOnlyImporter(file2) {
73057
- const support = supportForFile(file2);
73398
+ const support = supportForFileWithoutHeaderSample(file2);
73058
73399
  return support ? isGraphOnlyLanguage(support.id) : false;
73059
73400
  }
73060
73401
  function getUnresolvedImports(graph, opts = {}) {
73061
73402
  const unresolved = /* @__PURE__ */ new Map();
73062
73403
  const classificationCache = /* @__PURE__ */ new Map();
73404
+ const graphOnlyImporterCache = /* @__PURE__ */ new Map();
73063
73405
  for (const edge of graph.edges) {
73064
73406
  if (edge.to.type !== "external")
73065
73407
  continue;
73066
- if (!opts.includeGraphOnly && isGraphOnlyImporter(edge.from))
73067
- continue;
73408
+ if (!opts.includeGraphOnly) {
73409
+ let graphOnly = graphOnlyImporterCache.get(edge.from);
73410
+ if (graphOnly === void 0) {
73411
+ graphOnly = isGraphOnlyImporter(edge.from);
73412
+ graphOnlyImporterCache.set(edge.from, graphOnly);
73413
+ }
73414
+ if (graphOnly)
73415
+ continue;
73416
+ }
73068
73417
  if (isNodeBuiltinSpecifier(edge.to.name) || isNodeBuiltinSpecifier(edge.raw))
73069
73418
  continue;
73070
73419
  const classificationKey = `${edge.from}\0${edge.to.name}\0${edge.raw}\0${opts.projectRoot ?? ""}`;
@@ -74135,7 +74484,9 @@ function detectQueryIndexSurface(relativePath3) {
74135
74484
  return "code";
74136
74485
  }
74137
74486
  function buildQueryTextChunks(file2, text) {
74138
- const support = supportForFile(file2);
74487
+ return chunkQueryText(file2, text, supportForFileWithSource(file2, text));
74488
+ }
74489
+ function chunkQueryText(file2, text, support) {
74139
74490
  const languageId = support ? CHUNK_LANGUAGE_ALIASES[support.id] ?? support.id : void 0;
74140
74491
  const language = languageId ? LANG_CONFIGS[languageId] : void 0;
74141
74492
  if (language) {
@@ -74173,13 +74524,13 @@ function buildQueryTextChunks(file2, text) {
74173
74524
  async function prepareQueryIndexFile(input) {
74174
74525
  try {
74175
74526
  const stat = await fs32.stat(input.absolutePath);
74176
- const support = supportForFile(input.absolutePath);
74177
74527
  if (stat.size > MAX_QUERY_INDEX_TEXT_BYTES) {
74528
+ const support2 = supportForFile(input.absolutePath);
74178
74529
  return {
74179
74530
  path: input.path,
74180
74531
  sourceIdentity: input.sourceIdentity,
74181
74532
  surface: detectQueryIndexSurface(input.path),
74182
- ...support ? { language: support.id } : {},
74533
+ ...support2 ? { language: support2.id } : {},
74183
74534
  byteLength: stat.size,
74184
74535
  lineCount: 0,
74185
74536
  normalizedText: compressQueryText(""),
@@ -74188,6 +74539,7 @@ async function prepareQueryIndexFile(input) {
74188
74539
  };
74189
74540
  }
74190
74541
  const text = await fs32.readFile(input.absolutePath, "utf8");
74542
+ const support = supportForFileWithSource(input.absolutePath, text);
74191
74543
  return {
74192
74544
  path: input.path,
74193
74545
  sourceIdentity: input.sourceIdentity,
@@ -74196,7 +74548,7 @@ async function prepareQueryIndexFile(input) {
74196
74548
  byteLength: stat.size,
74197
74549
  lineCount: text ? text.split(/\r?\n/).length : 0,
74198
74550
  normalizedText: compressQueryText(normalizeQuerySearchText(text)),
74199
- chunks: buildQueryTextChunks(input.absolutePath, text).map((chunk) => ({
74551
+ chunks: chunkQueryText(input.absolutePath, text, support).map((chunk) => ({
74200
74552
  ...chunk,
74201
74553
  text: compressQueryText(chunk.text)
74202
74554
  })),
@@ -82485,15 +82837,16 @@ function validateNewName(snapshot, def, newName) {
82485
82837
  const file2 = normalizeAgentFilePath(snapshot.root, def.file);
82486
82838
  const conflicts = [];
82487
82839
  const invalidPathCharacters = newName.includes("/") || newName.includes("\\") || newName.includes("\0");
82488
- const support = supportForFile(def.file, snapshot.index.languageExtensions);
82840
+ const support = supportForFileWithoutHeaderSample(def.file, snapshot.index.languageExtensions);
82489
82841
  const allowsDollar = support?.id === "js" || support?.id === "ts" || support?.id === "tsx";
82490
82842
  const identifierPattern = allowsDollar ? /^[$_\p{ID_Start}](?:[$_\p{ID_Continue}]|\u200c|\u200d)*$/u : /^[_\p{ID_Start}][_\p{ID_Continue}]*$/u;
82491
82843
  if (!newName || invalidPathCharacters || !identifierPattern.test(newName)) {
82844
+ const displaySupport = supportForFile(def.file, snapshot.index.languageExtensions);
82492
82845
  conflicts.push({
82493
82846
  file: file2,
82494
82847
  location: { file: file2, range: def.range },
82495
82848
  reason: "invalid_identifier",
82496
- message: `"${newName}" is not a valid ${support?.id ?? "conservative"} identifier.`
82849
+ message: `"${newName}" is not a valid ${displaySupport?.id ?? "conservative"} identifier.`
82497
82850
  });
82498
82851
  }
82499
82852
  if (newName !== def.localName && newName.toLocaleLowerCase() === def.localName.toLocaleLowerCase()) {