@colbymchenry/codegraph-win32-arm64 1.2.0 → 1.3.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 (209) hide show
  1. package/lib/dist/bin/codegraph.d.ts +1 -1
  2. package/lib/dist/bin/codegraph.d.ts.map +1 -1
  3. package/lib/dist/bin/codegraph.js +232 -61
  4. package/lib/dist/bin/codegraph.js.map +1 -1
  5. package/lib/dist/bin/command-supervision.d.ts.map +1 -1
  6. package/lib/dist/bin/command-supervision.js +4 -1
  7. package/lib/dist/bin/command-supervision.js.map +1 -1
  8. package/lib/dist/db/migrations.d.ts +1 -1
  9. package/lib/dist/db/migrations.d.ts.map +1 -1
  10. package/lib/dist/db/migrations.js +19 -1
  11. package/lib/dist/db/migrations.js.map +1 -1
  12. package/lib/dist/db/queries.d.ts +51 -0
  13. package/lib/dist/db/queries.d.ts.map +1 -1
  14. package/lib/dist/db/queries.js +141 -0
  15. package/lib/dist/db/queries.js.map +1 -1
  16. package/lib/dist/db/schema.sql +19 -0
  17. package/lib/dist/directory.d.ts +9 -5
  18. package/lib/dist/directory.d.ts.map +1 -1
  19. package/lib/dist/directory.js +210 -19
  20. package/lib/dist/directory.js.map +1 -1
  21. package/lib/dist/extraction/cfml-extractor.d.ts +107 -0
  22. package/lib/dist/extraction/cfml-extractor.d.ts.map +1 -0
  23. package/lib/dist/extraction/cfml-extractor.js +494 -0
  24. package/lib/dist/extraction/cfml-extractor.js.map +1 -0
  25. package/lib/dist/extraction/grammars.d.ts +9 -0
  26. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  27. package/lib/dist/extraction/grammars.js +115 -3
  28. package/lib/dist/extraction/grammars.js.map +1 -1
  29. package/lib/dist/extraction/index.d.ts +46 -1
  30. package/lib/dist/extraction/index.d.ts.map +1 -1
  31. package/lib/dist/extraction/index.js +254 -3
  32. package/lib/dist/extraction/index.js.map +1 -1
  33. package/lib/dist/extraction/languages/arkts.d.ts +3 -0
  34. package/lib/dist/extraction/languages/arkts.d.ts.map +1 -0
  35. package/lib/dist/extraction/languages/arkts.js +127 -0
  36. package/lib/dist/extraction/languages/arkts.js.map +1 -0
  37. package/lib/dist/extraction/languages/c-cpp.d.ts +42 -0
  38. package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  39. package/lib/dist/extraction/languages/c-cpp.js +369 -4
  40. package/lib/dist/extraction/languages/c-cpp.js.map +1 -1
  41. package/lib/dist/extraction/languages/cfquery.d.ts +12 -0
  42. package/lib/dist/extraction/languages/cfquery.d.ts.map +1 -0
  43. package/lib/dist/extraction/languages/cfquery.js +28 -0
  44. package/lib/dist/extraction/languages/cfquery.js.map +1 -0
  45. package/lib/dist/extraction/languages/cfscript.d.ts +3 -0
  46. package/lib/dist/extraction/languages/cfscript.d.ts.map +1 -0
  47. package/lib/dist/extraction/languages/cfscript.js +73 -0
  48. package/lib/dist/extraction/languages/cfscript.js.map +1 -0
  49. package/lib/dist/extraction/languages/cobol.d.ts +33 -0
  50. package/lib/dist/extraction/languages/cobol.d.ts.map +1 -0
  51. package/lib/dist/extraction/languages/cobol.js +499 -0
  52. package/lib/dist/extraction/languages/cobol.js.map +1 -0
  53. package/lib/dist/extraction/languages/erlang.d.ts +3 -0
  54. package/lib/dist/extraction/languages/erlang.d.ts.map +1 -0
  55. package/lib/dist/extraction/languages/erlang.js +350 -0
  56. package/lib/dist/extraction/languages/erlang.js.map +1 -0
  57. package/lib/dist/extraction/languages/index.d.ts.map +1 -1
  58. package/lib/dist/extraction/languages/index.js +18 -0
  59. package/lib/dist/extraction/languages/index.js.map +1 -1
  60. package/lib/dist/extraction/languages/nix.d.ts +3 -0
  61. package/lib/dist/extraction/languages/nix.d.ts.map +1 -0
  62. package/lib/dist/extraction/languages/nix.js +294 -0
  63. package/lib/dist/extraction/languages/nix.js.map +1 -0
  64. package/lib/dist/extraction/languages/solidity.d.ts +3 -0
  65. package/lib/dist/extraction/languages/solidity.d.ts.map +1 -0
  66. package/lib/dist/extraction/languages/solidity.js +293 -0
  67. package/lib/dist/extraction/languages/solidity.js.map +1 -0
  68. package/lib/dist/extraction/languages/terraform.d.ts +3 -0
  69. package/lib/dist/extraction/languages/terraform.d.ts.map +1 -0
  70. package/lib/dist/extraction/languages/terraform.js +641 -0
  71. package/lib/dist/extraction/languages/terraform.js.map +1 -0
  72. package/lib/dist/extraction/languages/vbnet.d.ts +11 -0
  73. package/lib/dist/extraction/languages/vbnet.d.ts.map +1 -0
  74. package/lib/dist/extraction/languages/vbnet.js +141 -0
  75. package/lib/dist/extraction/languages/vbnet.js.map +1 -0
  76. package/lib/dist/extraction/mybatis-extractor.d.ts +30 -10
  77. package/lib/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  78. package/lib/dist/extraction/mybatis-extractor.js +140 -38
  79. package/lib/dist/extraction/mybatis-extractor.js.map +1 -1
  80. package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  81. package/lib/dist/extraction/tree-sitter-helpers.js +1 -0
  82. package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -1
  83. package/lib/dist/extraction/tree-sitter-types.d.ts +3 -1
  84. package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  85. package/lib/dist/extraction/tree-sitter.d.ts +38 -0
  86. package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
  87. package/lib/dist/extraction/tree-sitter.js +753 -10
  88. package/lib/dist/extraction/tree-sitter.js.map +1 -1
  89. package/lib/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  90. package/lib/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  91. package/lib/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  92. package/lib/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  93. package/lib/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  94. package/lib/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  95. package/lib/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  96. package/lib/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  97. package/lib/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  98. package/lib/dist/index.d.ts +63 -1
  99. package/lib/dist/index.d.ts.map +1 -1
  100. package/lib/dist/index.js +306 -2
  101. package/lib/dist/index.js.map +1 -1
  102. package/lib/dist/installer/index.d.ts.map +1 -1
  103. package/lib/dist/installer/index.js +3 -1
  104. package/lib/dist/installer/index.js.map +1 -1
  105. package/lib/dist/mcp/daemon.d.ts +25 -3
  106. package/lib/dist/mcp/daemon.d.ts.map +1 -1
  107. package/lib/dist/mcp/daemon.js +39 -7
  108. package/lib/dist/mcp/daemon.js.map +1 -1
  109. package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  110. package/lib/dist/mcp/dynamic-boundaries.js +2 -1
  111. package/lib/dist/mcp/dynamic-boundaries.js.map +1 -1
  112. package/lib/dist/mcp/early-ppid.d.ts +26 -0
  113. package/lib/dist/mcp/early-ppid.d.ts.map +1 -0
  114. package/lib/dist/mcp/early-ppid.js +29 -0
  115. package/lib/dist/mcp/early-ppid.js.map +1 -0
  116. package/lib/dist/mcp/index.d.ts.map +1 -1
  117. package/lib/dist/mcp/index.js +21 -4
  118. package/lib/dist/mcp/index.js.map +1 -1
  119. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  120. package/lib/dist/mcp/proxy.js +21 -3
  121. package/lib/dist/mcp/proxy.js.map +1 -1
  122. package/lib/dist/mcp/startup-handshake.d.ts +44 -0
  123. package/lib/dist/mcp/startup-handshake.d.ts.map +1 -0
  124. package/lib/dist/mcp/startup-handshake.js +73 -0
  125. package/lib/dist/mcp/startup-handshake.js.map +1 -0
  126. package/lib/dist/mcp/tools.d.ts +22 -0
  127. package/lib/dist/mcp/tools.d.ts.map +1 -1
  128. package/lib/dist/mcp/tools.js +74 -9
  129. package/lib/dist/mcp/tools.js.map +1 -1
  130. package/lib/dist/project-config.d.ts +38 -0
  131. package/lib/dist/project-config.d.ts.map +1 -1
  132. package/lib/dist/project-config.js +101 -2
  133. package/lib/dist/project-config.js.map +1 -1
  134. package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  135. package/lib/dist/resolution/callback-synthesizer.js +672 -0
  136. package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
  137. package/lib/dist/resolution/frameworks/cics.d.ts +20 -0
  138. package/lib/dist/resolution/frameworks/cics.d.ts.map +1 -0
  139. package/lib/dist/resolution/frameworks/cics.js +90 -0
  140. package/lib/dist/resolution/frameworks/cics.js.map +1 -0
  141. package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
  142. package/lib/dist/resolution/frameworks/index.js +6 -0
  143. package/lib/dist/resolution/frameworks/index.js.map +1 -1
  144. package/lib/dist/resolution/frameworks/java.d.ts.map +1 -1
  145. package/lib/dist/resolution/frameworks/java.js +14 -6
  146. package/lib/dist/resolution/frameworks/java.js.map +1 -1
  147. package/lib/dist/resolution/frameworks/terraform.d.ts +38 -0
  148. package/lib/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  149. package/lib/dist/resolution/frameworks/terraform.js +277 -0
  150. package/lib/dist/resolution/frameworks/terraform.js.map +1 -0
  151. package/lib/dist/resolution/import-resolver.d.ts +7 -0
  152. package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
  153. package/lib/dist/resolution/import-resolver.js +129 -4
  154. package/lib/dist/resolution/import-resolver.js.map +1 -1
  155. package/lib/dist/resolution/index.d.ts +39 -6
  156. package/lib/dist/resolution/index.d.ts.map +1 -1
  157. package/lib/dist/resolution/index.js +287 -50
  158. package/lib/dist/resolution/index.js.map +1 -1
  159. package/lib/dist/resolution/name-matcher.d.ts +0 -3
  160. package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
  161. package/lib/dist/resolution/name-matcher.js +225 -33
  162. package/lib/dist/resolution/name-matcher.js.map +1 -1
  163. package/lib/dist/resolution/strip-comments.d.ts +1 -1
  164. package/lib/dist/resolution/strip-comments.d.ts.map +1 -1
  165. package/lib/dist/resolution/strip-comments.js +49 -0
  166. package/lib/dist/resolution/strip-comments.js.map +1 -1
  167. package/lib/dist/resolution/types.d.ts +20 -0
  168. package/lib/dist/resolution/types.d.ts.map +1 -1
  169. package/lib/dist/resolution/workspace-packages.d.ts +10 -0
  170. package/lib/dist/resolution/workspace-packages.d.ts.map +1 -1
  171. package/lib/dist/resolution/workspace-packages.js +142 -4
  172. package/lib/dist/resolution/workspace-packages.js.map +1 -1
  173. package/lib/dist/search/identifier-segments.d.ts +60 -0
  174. package/lib/dist/search/identifier-segments.d.ts.map +1 -0
  175. package/lib/dist/search/identifier-segments.js +176 -0
  176. package/lib/dist/search/identifier-segments.js.map +1 -0
  177. package/lib/dist/sync/git-hooks.d.ts.map +1 -1
  178. package/lib/dist/sync/git-hooks.js +2 -0
  179. package/lib/dist/sync/git-hooks.js.map +1 -1
  180. package/lib/dist/sync/watcher.d.ts +10 -5
  181. package/lib/dist/sync/watcher.d.ts.map +1 -1
  182. package/lib/dist/sync/watcher.js +51 -14
  183. package/lib/dist/sync/watcher.js.map +1 -1
  184. package/lib/dist/sync/worktree.d.ts.map +1 -1
  185. package/lib/dist/sync/worktree.js +5 -0
  186. package/lib/dist/sync/worktree.js.map +1 -1
  187. package/lib/dist/types.d.ts +19 -1
  188. package/lib/dist/types.d.ts.map +1 -1
  189. package/lib/dist/types.js +10 -0
  190. package/lib/dist/types.js.map +1 -1
  191. package/lib/node_modules/.package-lock.json +1 -1
  192. package/lib/package.json +1 -1
  193. package/package.json +1 -1
  194. package/lib/dist/reasoning/config.d.ts +0 -45
  195. package/lib/dist/reasoning/config.d.ts.map +0 -1
  196. package/lib/dist/reasoning/config.js +0 -171
  197. package/lib/dist/reasoning/config.js.map +0 -1
  198. package/lib/dist/reasoning/credentials.d.ts +0 -5
  199. package/lib/dist/reasoning/credentials.d.ts.map +0 -1
  200. package/lib/dist/reasoning/credentials.js +0 -83
  201. package/lib/dist/reasoning/credentials.js.map +0 -1
  202. package/lib/dist/reasoning/login.d.ts +0 -21
  203. package/lib/dist/reasoning/login.d.ts.map +0 -1
  204. package/lib/dist/reasoning/login.js +0 -85
  205. package/lib/dist/reasoning/login.js.map +0 -1
  206. package/lib/dist/reasoning/reasoner.d.ts +0 -43
  207. package/lib/dist/reasoning/reasoner.d.ts.map +0 -1
  208. package/lib/dist/reasoning/reasoner.js +0 -308
  209. package/lib/dist/reasoning/reasoner.js.map +0 -1
@@ -240,6 +240,17 @@ class ReferenceResolver {
240
240
  nameCache; // name → nodes cache
241
241
  lowerNameCache; // lower(name) → nodes cache
242
242
  qualifiedNameCache; // qualified_name → nodes cache
243
+ fileLinesCache; // file → split lines cache
244
+ methodMatchCache; // lang\0Type::method → matching method nodes
245
+ // Node kinds are a small fixed set (~24), so this is a plain Map, not an LRU.
246
+ // getNodesByKind returns the FULL node list for a kind; it was previously
247
+ // uncached — a per-ref `SELECT * FROM nodes WHERE kind=?` + row-mapping. Called
248
+ // for every dotted call ref by the Spring resolver (constants) and every
249
+ // `hook_` ref by the Drupal resolver (functions), that scan dominated
250
+ // resolution on large repos (#1180). The node set is stable within a
251
+ // resolution pass (same lifetime assumption as nameCache); clearCaches() resets
252
+ // it between passes. Callers must treat the returned array as read-only.
253
+ nodesByKindCache = new Map();
243
254
  knownNames = null; // all known symbol names for fast pre-filtering
244
255
  knownFiles = null;
245
256
  cachesWarmed = false;
@@ -265,6 +276,10 @@ class ReferenceResolver {
265
276
  this.nameCache = new lru_cache_1.LRUCache(limit);
266
277
  this.lowerNameCache = new lru_cache_1.LRUCache(limit);
267
278
  this.qualifiedNameCache = new lru_cache_1.LRUCache(limit);
279
+ // Split-lines arrays are heavier than content strings; refs arrive
280
+ // file-ordered, so a small cache still hits nearly always.
281
+ this.fileLinesCache = new lru_cache_1.LRUCache(contentLimit);
282
+ this.methodMatchCache = new lru_cache_1.LRUCache(limit);
268
283
  this.context = this.createContext();
269
284
  }
270
285
  /**
@@ -331,10 +346,30 @@ class ReferenceResolver {
331
346
  this.nameCache.clear();
332
347
  this.lowerNameCache.clear();
333
348
  this.qualifiedNameCache.clear();
349
+ this.fileLinesCache.clear();
350
+ this.methodMatchCache.clear();
351
+ this.nodesByKindCache.clear();
334
352
  this.knownNames = null;
335
353
  this.knownFiles = null;
336
354
  this.cachesWarmed = false;
337
355
  }
356
+ /** `readFile` through the LRU content cache (null = read failed, also cached). */
357
+ readFileCached(filePath) {
358
+ if (this.fileCache.has(filePath)) {
359
+ return this.fileCache.get(filePath);
360
+ }
361
+ const fullPath = path.join(this.projectRoot, filePath);
362
+ try {
363
+ const content = fs.readFileSync(fullPath, 'utf-8');
364
+ this.fileCache.set(filePath, content);
365
+ return content;
366
+ }
367
+ catch (error) {
368
+ (0, errors_1.logDebug)('Failed to read file for resolution', { filePath, error: String(error) });
369
+ this.fileCache.set(filePath, null);
370
+ return null;
371
+ }
372
+ }
338
373
  /**
339
374
  * Create the resolution context
340
375
  */
@@ -354,6 +389,30 @@ class ReferenceResolver {
354
389
  this.nameCache.set(name, result);
355
390
  return result;
356
391
  },
392
+ getMethodMatches: (typeName, methodName, language) => {
393
+ const key = `${language} ${typeName}::${methodName}`;
394
+ const cached = this.methodMatchCache.get(key);
395
+ if (cached !== undefined)
396
+ return cached;
397
+ let candidates = this.nameCache.get(methodName);
398
+ if (candidates === undefined) {
399
+ candidates = this.queries.getNodesByName(methodName);
400
+ this.nameCache.set(methodName, candidates);
401
+ }
402
+ const want = `${typeName}::${methodName}`;
403
+ const matches = [];
404
+ for (const m of candidates) {
405
+ if (m.kind !== 'method')
406
+ continue;
407
+ if (m.language !== language)
408
+ continue;
409
+ const qn = m.qualifiedName;
410
+ if (qn === want || qn.endsWith(`::${want}`))
411
+ matches.push(m);
412
+ }
413
+ this.methodMatchCache.set(key, matches);
414
+ return matches;
415
+ },
357
416
  getNodesByQualifiedName: (qualifiedName) => {
358
417
  const cached = this.qualifiedNameCache.get(qualifiedName);
359
418
  if (cached !== undefined)
@@ -363,7 +422,12 @@ class ReferenceResolver {
363
422
  return result;
364
423
  },
365
424
  getNodesByKind: (kind) => {
366
- return this.queries.getNodesByKind(kind);
425
+ const cached = this.nodesByKindCache.get(kind);
426
+ if (cached !== undefined)
427
+ return cached;
428
+ const result = this.queries.getNodesByKind(kind);
429
+ this.nodesByKindCache.set(kind, result);
430
+ return result;
367
431
  },
368
432
  fileExists: (filePath) => {
369
433
  // Check pre-built known files set first (O(1))
@@ -383,21 +447,15 @@ class ReferenceResolver {
383
447
  return false;
384
448
  }
385
449
  },
386
- readFile: (filePath) => {
387
- if (this.fileCache.has(filePath)) {
388
- return this.fileCache.get(filePath);
389
- }
390
- const fullPath = path.join(this.projectRoot, filePath);
391
- try {
392
- const content = fs.readFileSync(fullPath, 'utf-8');
393
- this.fileCache.set(filePath, content);
394
- return content;
395
- }
396
- catch (error) {
397
- (0, errors_1.logDebug)('Failed to read file for resolution', { filePath, error: String(error) });
398
- this.fileCache.set(filePath, null);
399
- return null;
400
- }
450
+ readFile: (filePath) => this.readFileCached(filePath),
451
+ getFileLines: (filePath) => {
452
+ const cached = this.fileLinesCache.get(filePath);
453
+ if (cached !== undefined)
454
+ return cached;
455
+ const source = this.readFileCached(filePath);
456
+ const lines = source === null ? null : source.split(/\r?\n/);
457
+ this.fileLinesCache.set(filePath, lines);
458
+ return lines;
401
459
  },
402
460
  getProjectRoot: () => this.projectRoot,
403
461
  getAllFiles: () => {
@@ -500,7 +558,7 @@ class ReferenceResolver {
500
558
  // `.ts` index barrel and silently break the chain (#629). Re-key
501
559
  // the parse on the barrel's extension so the chase works no matter
502
560
  // what kind of file imports through it.
503
- const isJsFamily = /\.(?:d\.ts|[cm]?tsx?|[cm]?jsx?)$/i.test(filePath);
561
+ const isJsFamily = /\.(?:d\.ts|[cm]?tsx?|[cm]?jsx?|ets)$/i.test(filePath);
504
562
  const reExports = (0, import_resolver_1.extractReExports)(content, isJsFamily ? 'typescript' : language);
505
563
  this.reExportCache.set(filePath, reExports);
506
564
  return reExports;
@@ -667,13 +725,36 @@ class ReferenceResolver {
667
725
  if (this.isBuiltInOrExternal(ref)) {
668
726
  return null;
669
727
  }
728
+ // CFML component paths in inheritance (#1152): `extends="coldbox.system.web.
729
+ // Controller"` names the supertype by its dot-separated path (or `extends=
730
+ // "../base"` by relative file path) — the graph indexes the class under its
731
+ // final segment only, so these die at the fast pre-filter below and never
732
+ // resolved. Handled by a dedicated path-corroborated matcher, gated to
733
+ // inheritance refs only (a dotted `calls` ref is a member-access chain, not
734
+ // a component path). No fallthrough on miss: the full path string can only
735
+ // ever mis-match downstream, and an unresolvable supertype usually lives in
736
+ // an out-of-repo library (mxunit, testbox) — silent beats wrong.
737
+ if ((ref.language === 'cfml' || ref.language === 'cfscript') &&
738
+ (ref.referenceKind === 'extends' || ref.referenceKind === 'implements') &&
739
+ (ref.referenceName.includes('.') || ref.referenceName.includes('/'))) {
740
+ return this.resolveCfmlComponentPath(ref);
741
+ }
670
742
  // Fast pre-filter: skip if no symbol with this name exists anywhere
671
743
  // AND the name doesn't match a local import. The import escape is
672
744
  // necessary because re-export rename chains (`import { login }
673
745
  // from './barrel'` where the barrel has `export { signIn as login }
674
746
  // from './auth'`) intentionally call a name that has no
675
747
  // declaration anywhere — only the renamed upstream symbol does.
676
- if (!this.hasAnyPossibleMatch(ref.referenceName) &&
748
+ // ArkTS chained-attribute refs carry a leading dot (`.titleStyle`) that
749
+ // routes them to the decorator-gated matcher; the symbol itself is
750
+ // indexed under the bare name, so the existence check strips the dot.
751
+ // Nix static path imports (`import ./x.nix`) name a FILE, not a symbol —
752
+ // they bypass the symbol-existence check and resolve via resolveViaImport.
753
+ const existenceName = ref.language === 'arkts' && ref.referenceName.startsWith('.')
754
+ ? ref.referenceName.slice(1)
755
+ : ref.referenceName;
756
+ if (!(0, import_resolver_1.isNixPathImportRef)(ref) &&
757
+ !this.hasAnyPossibleMatch(existenceName) &&
677
758
  !this.matchesAnyImport(ref) &&
678
759
  !this.frameworks.some((f) => f.claimsReference?.(ref.referenceName))) {
679
760
  return null;
@@ -739,13 +820,40 @@ class ReferenceResolver {
739
820
  // If that didn't find the file, do NOT fall back to the symbol
740
821
  // name-matcher — it would mis-connect e.g. "inc/db.php" to an unrelated
741
822
  // db.php elsewhere in the tree (a wrong edge is worse than none, #660).
742
- if ((0, import_resolver_1.isPhpIncludePathRef)(ref)) {
823
+ // Terraform refs are directory-scoped by language semantics — the
824
+ // framework resolver IS the whole rulebook (`var.X` can never legally
825
+ // bind outside its module directory), so the name-matcher's
826
+ // qualified-name fallback would only ever add wrong cross-module edges.
827
+ // Nix static path imports are file references for the same reason —
828
+ // falling through would let "./x.nix" name-match an unrelated node.
829
+ if ((0, import_resolver_1.isPhpIncludePathRef)(ref) || (0, import_resolver_1.isCobolCopybookRef)(ref) || (0, import_resolver_1.isNixPathImportRef)(ref) || ref.language === 'terraform') {
743
830
  return candidates.length > 0
744
831
  ? candidates.reduce((best, curr) => curr.confidence > best.confidence ? curr : best)
745
832
  : null;
746
833
  }
747
834
  // Strategy 3: Try name matching
748
- const nameResult = this.gateLanguage((0, name_matcher_1.matchReference)(ref, this.context), ref);
835
+ let nameResult = this.gateLanguage((0, name_matcher_1.matchReference)(ref, this.context), ref);
836
+ // Nix has no ambient cross-file namespace — a callee binds lexically
837
+ // (same file) or through explicit import/callPackage wiring (the import
838
+ // path above). A cross-file name match is wrong by construction: every
839
+ // module `inherit (lib) mkOption`s the same nixpkgs helpers, so the
840
+ // matcher would link each `mkOption` call to whichever file's inherit
841
+ // binding it happened to pick. Same-file matches only.
842
+ if (nameResult) {
843
+ const target = this.queries.getNodeById(nameResult.targetNodeId);
844
+ if (ref.language === 'nix') {
845
+ if (!target || target.filePath !== ref.filePath) {
846
+ nameResult = null;
847
+ }
848
+ }
849
+ else if (target && target.language === 'nix') {
850
+ // The reverse direction is just as impossible: no other language can
851
+ // symbolically call into a .nix binding (interop is eval/CLI, never a
852
+ // linkable symbol) — without this, a Python script's `split()` lands
853
+ // on some module's `split = ...` binding as a low-confidence match.
854
+ nameResult = null;
855
+ }
856
+ }
749
857
  if (nameResult) {
750
858
  candidates.push(nameResult);
751
859
  }
@@ -832,6 +940,20 @@ class ReferenceResolver {
832
940
  referenceKind: r.original.referenceKind,
833
941
  })));
834
942
  }
943
+ // Delete unresolvable refs too — parity with resolveAndPersistBatched.
944
+ // Keeping them bought nothing: a ref is only ever retried when its file
945
+ // is re-extracted, which cascade-deletes and re-inserts its rows anyway.
946
+ // And it broke the #1187 orphan sweep's invariant — after a COMPLETED
947
+ // pass the table must hold nothing that pass processed, so that any row
948
+ // still present belongs to an interrupted run and the sweep can key off
949
+ // a bare row count.
950
+ if (result.unresolved.length > 0) {
951
+ this.queries.deleteSpecificResolvedReferences(result.unresolved.map((r) => ({
952
+ fromNodeId: r.fromNodeId,
953
+ referenceName: r.referenceName,
954
+ referenceKind: r.referenceKind,
955
+ })));
956
+ }
835
957
  return result;
836
958
  }
837
959
  /**
@@ -882,37 +1004,53 @@ class ReferenceResolver {
882
1004
  return edges.length;
883
1005
  }
884
1006
  /**
885
- * Resolve one batch in smaller sub-chunks, yielding to the event loop between
886
- * them so the #850 liveness heartbeat can fire on a slow/dense batch (#1091).
887
- * Behaviourally identical to a single `resolveAll(batch)`: `warmCaches()` is
888
- * idempotent (guarded) and `resolveOne` is independent per ref, so splitting
889
- * and re-merging changes only timing, never which edges get created. Falls
890
- * through to a plain `resolveAll` when the batch is already small.
1007
+ * Resolve one batch with a yield checkpoint between EVERY ref so the #850
1008
+ * liveness heartbeat can fire on a slow/dense batch (#1091). The checkpoint
1009
+ * granularity is per-ref not per-N-refs because per-ref cost is unbounded
1010
+ * in the worst case (a collision-heavy method name whose candidate set misses
1011
+ * the LRU re-fetches tens of thousands of rows): any fixed N multiplies that
1012
+ * worst case into the watchdog window, which is how v1.2.0 still got killed
1013
+ * at "Resolving refs" on large Java monorepos (#1122). `maybeYield()` is a
1014
+ * ~ns time check when under budget, so per-ref checkpoints cost nothing.
1015
+ * Behaviourally identical to `resolveAll(batch)`: `warmCaches()` is
1016
+ * idempotent (guarded) and `resolveOne` is independent per ref, so yielding
1017
+ * between refs changes only timing, never which edges get created.
891
1018
  */
892
- async resolveBatchYielding(batch, maybeYield, subChunkSize = 500) {
893
- if (batch.length <= subChunkSize)
894
- return this.resolveAll(batch);
1019
+ async resolveBatchYielding(batch, maybeYield) {
1020
+ this.warmCaches();
895
1021
  const resolved = [];
896
1022
  const unresolved = [];
897
1023
  const byMethod = {};
898
- let total = 0;
899
- let resolvedCount = 0;
900
- let unresolvedCount = 0;
901
- for (let i = 0; i < batch.length; i += subChunkSize) {
902
- const chunk = this.resolveAll(batch.slice(i, i + subChunkSize));
903
- for (const r of chunk.resolved)
904
- resolved.push(r);
905
- for (const u of chunk.unresolved)
906
- unresolved.push(u);
907
- total += chunk.stats.total;
908
- resolvedCount += chunk.stats.resolved;
909
- unresolvedCount += chunk.stats.unresolved;
910
- for (const [m, c] of Object.entries(chunk.stats.byMethod)) {
911
- byMethod[m] = (byMethod[m] || 0) + c;
1024
+ for (const raw of batch) {
1025
+ const ref = {
1026
+ fromNodeId: raw.fromNodeId,
1027
+ referenceName: raw.referenceName,
1028
+ referenceKind: raw.referenceKind,
1029
+ line: raw.line,
1030
+ column: raw.column,
1031
+ filePath: raw.filePath || this.getFilePathFromNodeId(raw.fromNodeId),
1032
+ language: raw.language || this.getLanguageFromNodeId(raw.fromNodeId),
1033
+ };
1034
+ const result = this.resolveOne(ref);
1035
+ if (result) {
1036
+ resolved.push(result);
1037
+ byMethod[result.resolvedBy] = (byMethod[result.resolvedBy] || 0) + 1;
1038
+ }
1039
+ else {
1040
+ unresolved.push(ref);
912
1041
  }
913
1042
  await maybeYield();
914
1043
  }
915
- return { resolved, unresolved, stats: { total, resolved: resolvedCount, unresolved: unresolvedCount, byMethod } };
1044
+ return {
1045
+ resolved,
1046
+ unresolved,
1047
+ stats: {
1048
+ total: batch.length,
1049
+ resolved: resolved.length,
1050
+ unresolved: unresolved.length,
1051
+ byMethod,
1052
+ },
1053
+ };
916
1054
  }
917
1055
  /**
918
1056
  * Resolve and persist in batches to keep memory bounded.
@@ -976,11 +1114,13 @@ class ReferenceResolver {
976
1114
  onProgress?.(processed, total);
977
1115
  // Yield so progress UI can render between batches
978
1116
  await new Promise(resolve => setImmediate(resolve));
979
- // If nothing was resolved or removed in this batch, we'd loop forever
980
- // on the same rows. Break to avoid infinite loop.
981
- if (result.resolved.length === 0 && result.unresolved.length === batch.length) {
982
- break;
983
- }
1117
+ // NOTE: there used to be an extra early break here when a batch resolved
1118
+ // nothing (`result.unresolved.length === batch.length`). That was wrong:
1119
+ // an all-unresolvable batch still DELETES its rows (progress), yet the
1120
+ // break abandoned every batch after it in the same run — on a repo whose
1121
+ // first 5000 refs are all external/stdlib calls, resolution stopped at
1122
+ // batch one and left the rest of the table as permanent orphans (#1187).
1123
+ // The count-based guard below catches the true no-progress case.
984
1124
  // Non-progress guard (defense-in-depth). Because we re-read from offset 0
985
1125
  // each pass, the unresolved_refs table MUST shrink every iteration — both
986
1126
  // resolved and unresolved refs are deleted above. If it didn't shrink, a
@@ -1022,11 +1162,18 @@ class ReferenceResolver {
1022
1162
  isBuiltInOrExternal(ref) {
1023
1163
  const name = ref.referenceName;
1024
1164
  const isJsTs = ref.language === 'typescript' || ref.language === 'javascript'
1025
- || ref.language === 'tsx' || ref.language === 'jsx';
1165
+ || ref.language === 'tsx' || ref.language === 'jsx' || ref.language === 'arkts';
1026
1166
  // JavaScript/TypeScript built-ins
1027
1167
  if (isJsTs && JS_BUILT_INS.has(name)) {
1028
1168
  return true;
1029
1169
  }
1170
+ // ArkTS resource-reference intrinsics — `$r('app.string.x')` /
1171
+ // `$rawfile('x.png')` are framework-provided and appear dozens of times
1172
+ // per UI file; without this they can resolve to a stray same-named
1173
+ // symbol (e.g. a checked-in hvigor wrapper's `$r`).
1174
+ if (ref.language === 'arkts' && (name === '$r' || name === '$rawfile')) {
1175
+ return true;
1176
+ }
1030
1177
  // Common JS/TS library calls (console.log, Math.floor, JSON.parse)
1031
1178
  if (isJsTs && (name.startsWith('console.') || name.startsWith('Math.') || name.startsWith('JSON.'))) {
1032
1179
  return true;
@@ -1191,6 +1338,96 @@ class ReferenceResolver {
1191
1338
  const target = found.values().next().value;
1192
1339
  return { original: ref, targetNodeId: target.id, confidence: 0.9, resolvedBy: 'import' };
1193
1340
  }
1341
+ /**
1342
+ * Resolve a CFML inheritance reference written as a component path (#1152).
1343
+ * Two forms exist in real code:
1344
+ *
1345
+ * - Dotted: `extends="coldbox.system.web.Controller"` — dots are directory
1346
+ * separators from the webroot or a CFML mapping. Mappings live in server
1347
+ * config / Application.cfc, so the leading segments may not exist in the
1348
+ * repo at all (in the coldbox repo itself the path is `system/web/
1349
+ * Controller.cfc` — the `coldbox.` root IS the repo). Matched by final
1350
+ * segment (the class), corroborated right-to-left against the candidate's
1351
+ * parent directories.
1352
+ * - Relative: `extends="../base"` / `extends="./base"` (the FW/1 style) —
1353
+ * resolved against the referencing file's own directory.
1354
+ *
1355
+ * Conservative by design: a candidate needs at least one corroborating
1356
+ * directory segment (a dotted path whose only same-named class sits in an
1357
+ * unrelated directory is almost always an out-of-repo library supertype —
1358
+ * mxunit/testbox/coldbox-as-dependency), and a corroboration tie yields no
1359
+ * edge. Directory comparison is case-insensitive (CFML path resolution is);
1360
+ * the class segment itself is matched exactly, which real code satisfies —
1361
+ * dotted paths are written to match the on-disk file name.
1362
+ */
1363
+ resolveCfmlComponentPath(ref) {
1364
+ const cfmlCandidates = (name) => this.context
1365
+ .getNodesByName(name)
1366
+ .filter((n) => (n.kind === 'class' || n.kind === 'interface') &&
1367
+ (n.language === 'cfml' || n.language === 'cfscript'));
1368
+ const norm = (p) => p.replace(/\\/g, '/').toLowerCase();
1369
+ // Relative-path form: `../base`, `./base`, `sub/thing` — resolve against
1370
+ // the referencing file's directory and require an exact (case-insensitive)
1371
+ // file match.
1372
+ if (ref.referenceName.includes('/')) {
1373
+ const rel = ref.referenceName.replace(/\.cfc$/i, '');
1374
+ const fromDir = ref.filePath.replace(/\\/g, '/').split('/').slice(0, -1);
1375
+ const parts = [...fromDir];
1376
+ for (const seg of rel.split('/')) {
1377
+ if (seg === '' || seg === '.')
1378
+ continue;
1379
+ if (seg === '..') {
1380
+ if (parts.length === 0)
1381
+ return null; // escapes the project root
1382
+ parts.pop();
1383
+ }
1384
+ else {
1385
+ parts.push(seg);
1386
+ }
1387
+ }
1388
+ const wantPath = norm(parts.join('/') + '.cfc');
1389
+ const className = parts[parts.length - 1];
1390
+ if (!className)
1391
+ return null;
1392
+ const target = cfmlCandidates(className).find((c) => norm(c.filePath) === wantPath);
1393
+ return target
1394
+ ? { original: ref, targetNodeId: target.id, confidence: 0.95, resolvedBy: 'file-path' }
1395
+ : null;
1396
+ }
1397
+ // Dotted form.
1398
+ const segments = ref.referenceName.split('.').map((s) => s.trim()).filter(Boolean);
1399
+ if (segments.length < 2)
1400
+ return null;
1401
+ const className = segments[segments.length - 1];
1402
+ const dirSegments = segments.slice(0, -1);
1403
+ let best = null;
1404
+ let bestScore = 0;
1405
+ let tie = false;
1406
+ for (const cand of cfmlCandidates(className)) {
1407
+ const dirs = cand.filePath.replace(/\\/g, '/').split('/').slice(0, -1);
1408
+ // Count matching directory segments right-to-left: for
1409
+ // `coldbox.system.web.Controller` vs `system/web/Controller.cfc`,
1410
+ // `web` and `system` match, then the repo root ends the run → score 2.
1411
+ let score = 0;
1412
+ while (score < dirSegments.length &&
1413
+ score < dirs.length &&
1414
+ dirSegments[dirSegments.length - 1 - score].toLowerCase() ===
1415
+ dirs[dirs.length - 1 - score].toLowerCase()) {
1416
+ score++;
1417
+ }
1418
+ if (score > bestScore) {
1419
+ best = cand;
1420
+ bestScore = score;
1421
+ tie = false;
1422
+ }
1423
+ else if (score === bestScore && score > 0) {
1424
+ tie = true;
1425
+ }
1426
+ }
1427
+ if (!best || bestScore === 0 || tie)
1428
+ return null;
1429
+ return { original: ref, targetNodeId: best.id, confidence: 0.9, resolvedBy: 'qualified-name' };
1430
+ }
1194
1431
  /**
1195
1432
  * Resolve a `this.<member>` function-as-value reference (#756/#808) to the
1196
1433
  * ENCLOSING CLASS's own member — never a same-named symbol elsewhere. The