@colbymchenry/codegraph-win32-x64 1.1.6 → 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 (219) 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 +14 -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 +59 -0
  38. package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  39. package/lib/dist/extraction/languages/c-cpp.js +499 -3
  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 +20 -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 +845 -16
  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/graph/traversal.d.ts.map +1 -1
  99. package/lib/dist/graph/traversal.js +76 -17
  100. package/lib/dist/graph/traversal.js.map +1 -1
  101. package/lib/dist/index.d.ts +63 -1
  102. package/lib/dist/index.d.ts.map +1 -1
  103. package/lib/dist/index.js +310 -6
  104. package/lib/dist/index.js.map +1 -1
  105. package/lib/dist/installer/index.d.ts.map +1 -1
  106. package/lib/dist/installer/index.js +3 -1
  107. package/lib/dist/installer/index.js.map +1 -1
  108. package/lib/dist/mcp/daemon.d.ts +25 -3
  109. package/lib/dist/mcp/daemon.d.ts.map +1 -1
  110. package/lib/dist/mcp/daemon.js +39 -7
  111. package/lib/dist/mcp/daemon.js.map +1 -1
  112. package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  113. package/lib/dist/mcp/dynamic-boundaries.js +2 -1
  114. package/lib/dist/mcp/dynamic-boundaries.js.map +1 -1
  115. package/lib/dist/mcp/early-ppid.d.ts +26 -0
  116. package/lib/dist/mcp/early-ppid.d.ts.map +1 -0
  117. package/lib/dist/mcp/early-ppid.js +29 -0
  118. package/lib/dist/mcp/early-ppid.js.map +1 -0
  119. package/lib/dist/mcp/index.d.ts.map +1 -1
  120. package/lib/dist/mcp/index.js +21 -4
  121. package/lib/dist/mcp/index.js.map +1 -1
  122. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  123. package/lib/dist/mcp/proxy.js +21 -3
  124. package/lib/dist/mcp/proxy.js.map +1 -1
  125. package/lib/dist/mcp/startup-handshake.d.ts +44 -0
  126. package/lib/dist/mcp/startup-handshake.d.ts.map +1 -0
  127. package/lib/dist/mcp/startup-handshake.js +73 -0
  128. package/lib/dist/mcp/startup-handshake.js.map +1 -0
  129. package/lib/dist/mcp/tools.d.ts +22 -0
  130. package/lib/dist/mcp/tools.d.ts.map +1 -1
  131. package/lib/dist/mcp/tools.js +74 -9
  132. package/lib/dist/mcp/tools.js.map +1 -1
  133. package/lib/dist/project-config.d.ts +38 -0
  134. package/lib/dist/project-config.d.ts.map +1 -1
  135. package/lib/dist/project-config.js +101 -2
  136. package/lib/dist/project-config.js.map +1 -1
  137. package/lib/dist/resolution/callback-synthesizer.d.ts +1 -1
  138. package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  139. package/lib/dist/resolution/callback-synthesizer.js +743 -11
  140. package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
  141. package/lib/dist/resolution/cooperative-yield.d.ts +32 -0
  142. package/lib/dist/resolution/cooperative-yield.d.ts.map +1 -0
  143. package/lib/dist/resolution/cooperative-yield.js +42 -0
  144. package/lib/dist/resolution/cooperative-yield.js.map +1 -0
  145. package/lib/dist/resolution/frameworks/cics.d.ts +20 -0
  146. package/lib/dist/resolution/frameworks/cics.d.ts.map +1 -0
  147. package/lib/dist/resolution/frameworks/cics.js +90 -0
  148. package/lib/dist/resolution/frameworks/cics.js.map +1 -0
  149. package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
  150. package/lib/dist/resolution/frameworks/index.js +6 -0
  151. package/lib/dist/resolution/frameworks/index.js.map +1 -1
  152. package/lib/dist/resolution/frameworks/java.d.ts.map +1 -1
  153. package/lib/dist/resolution/frameworks/java.js +14 -6
  154. package/lib/dist/resolution/frameworks/java.js.map +1 -1
  155. package/lib/dist/resolution/frameworks/terraform.d.ts +38 -0
  156. package/lib/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  157. package/lib/dist/resolution/frameworks/terraform.js +277 -0
  158. package/lib/dist/resolution/frameworks/terraform.js.map +1 -0
  159. package/lib/dist/resolution/import-resolver.d.ts +7 -0
  160. package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
  161. package/lib/dist/resolution/import-resolver.js +129 -4
  162. package/lib/dist/resolution/import-resolver.js.map +1 -1
  163. package/lib/dist/resolution/index.d.ts +44 -2
  164. package/lib/dist/resolution/index.d.ts.map +1 -1
  165. package/lib/dist/resolution/index.js +335 -30
  166. package/lib/dist/resolution/index.js.map +1 -1
  167. package/lib/dist/resolution/name-matcher.d.ts +22 -3
  168. package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
  169. package/lib/dist/resolution/name-matcher.js +484 -39
  170. package/lib/dist/resolution/name-matcher.js.map +1 -1
  171. package/lib/dist/resolution/strip-comments.d.ts +1 -1
  172. package/lib/dist/resolution/strip-comments.d.ts.map +1 -1
  173. package/lib/dist/resolution/strip-comments.js +49 -0
  174. package/lib/dist/resolution/strip-comments.js.map +1 -1
  175. package/lib/dist/resolution/types.d.ts +20 -0
  176. package/lib/dist/resolution/types.d.ts.map +1 -1
  177. package/lib/dist/resolution/workspace-packages.d.ts +10 -0
  178. package/lib/dist/resolution/workspace-packages.d.ts.map +1 -1
  179. package/lib/dist/resolution/workspace-packages.js +142 -4
  180. package/lib/dist/resolution/workspace-packages.js.map +1 -1
  181. package/lib/dist/search/identifier-segments.d.ts +60 -0
  182. package/lib/dist/search/identifier-segments.d.ts.map +1 -0
  183. package/lib/dist/search/identifier-segments.js +176 -0
  184. package/lib/dist/search/identifier-segments.js.map +1 -0
  185. package/lib/dist/sync/git-hooks.d.ts.map +1 -1
  186. package/lib/dist/sync/git-hooks.js +2 -0
  187. package/lib/dist/sync/git-hooks.js.map +1 -1
  188. package/lib/dist/sync/watcher.d.ts +10 -5
  189. package/lib/dist/sync/watcher.d.ts.map +1 -1
  190. package/lib/dist/sync/watcher.js +51 -14
  191. package/lib/dist/sync/watcher.js.map +1 -1
  192. package/lib/dist/sync/worktree.d.ts.map +1 -1
  193. package/lib/dist/sync/worktree.js +5 -0
  194. package/lib/dist/sync/worktree.js.map +1 -1
  195. package/lib/dist/types.d.ts +19 -1
  196. package/lib/dist/types.d.ts.map +1 -1
  197. package/lib/dist/types.js +10 -0
  198. package/lib/dist/types.js.map +1 -1
  199. package/lib/dist/upgrade/index.js +1 -1
  200. package/lib/dist/upgrade/index.js.map +1 -1
  201. package/lib/node_modules/.package-lock.json +1 -1
  202. package/lib/package.json +1 -1
  203. package/package.json +1 -1
  204. package/lib/dist/reasoning/config.d.ts +0 -45
  205. package/lib/dist/reasoning/config.d.ts.map +0 -1
  206. package/lib/dist/reasoning/config.js +0 -171
  207. package/lib/dist/reasoning/config.js.map +0 -1
  208. package/lib/dist/reasoning/credentials.d.ts +0 -5
  209. package/lib/dist/reasoning/credentials.d.ts.map +0 -1
  210. package/lib/dist/reasoning/credentials.js +0 -83
  211. package/lib/dist/reasoning/credentials.js.map +0 -1
  212. package/lib/dist/reasoning/login.d.ts +0 -21
  213. package/lib/dist/reasoning/login.d.ts.map +0 -1
  214. package/lib/dist/reasoning/login.js +0 -85
  215. package/lib/dist/reasoning/login.js.map +0 -1
  216. package/lib/dist/reasoning/reasoner.d.ts +0 -43
  217. package/lib/dist/reasoning/reasoner.d.ts.map +0 -1
  218. package/lib/dist/reasoning/reasoner.js +0 -308
  219. package/lib/dist/reasoning/reasoner.js.map +0 -1
@@ -49,6 +49,7 @@ const name_matcher_1 = require("./name-matcher");
49
49
  const import_resolver_1 = require("./import-resolver");
50
50
  const frameworks_1 = require("./frameworks");
51
51
  const callback_synthesizer_1 = require("./callback-synthesizer");
52
+ const cooperative_yield_1 = require("./cooperative-yield");
52
53
  const path_aliases_1 = require("./path-aliases");
53
54
  const go_module_1 = require("./go-module");
54
55
  const workspace_packages_1 = require("./workspace-packages");
@@ -239,6 +240,17 @@ class ReferenceResolver {
239
240
  nameCache; // name → nodes cache
240
241
  lowerNameCache; // lower(name) → nodes cache
241
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();
242
254
  knownNames = null; // all known symbol names for fast pre-filtering
243
255
  knownFiles = null;
244
256
  cachesWarmed = false;
@@ -264,6 +276,10 @@ class ReferenceResolver {
264
276
  this.nameCache = new lru_cache_1.LRUCache(limit);
265
277
  this.lowerNameCache = new lru_cache_1.LRUCache(limit);
266
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);
267
283
  this.context = this.createContext();
268
284
  }
269
285
  /**
@@ -330,10 +346,30 @@ class ReferenceResolver {
330
346
  this.nameCache.clear();
331
347
  this.lowerNameCache.clear();
332
348
  this.qualifiedNameCache.clear();
349
+ this.fileLinesCache.clear();
350
+ this.methodMatchCache.clear();
351
+ this.nodesByKindCache.clear();
333
352
  this.knownNames = null;
334
353
  this.knownFiles = null;
335
354
  this.cachesWarmed = false;
336
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
+ }
337
373
  /**
338
374
  * Create the resolution context
339
375
  */
@@ -353,6 +389,30 @@ class ReferenceResolver {
353
389
  this.nameCache.set(name, result);
354
390
  return result;
355
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
+ },
356
416
  getNodesByQualifiedName: (qualifiedName) => {
357
417
  const cached = this.qualifiedNameCache.get(qualifiedName);
358
418
  if (cached !== undefined)
@@ -362,7 +422,12 @@ class ReferenceResolver {
362
422
  return result;
363
423
  },
364
424
  getNodesByKind: (kind) => {
365
- 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;
366
431
  },
367
432
  fileExists: (filePath) => {
368
433
  // Check pre-built known files set first (O(1))
@@ -382,21 +447,15 @@ class ReferenceResolver {
382
447
  return false;
383
448
  }
384
449
  },
385
- readFile: (filePath) => {
386
- if (this.fileCache.has(filePath)) {
387
- return this.fileCache.get(filePath);
388
- }
389
- const fullPath = path.join(this.projectRoot, filePath);
390
- try {
391
- const content = fs.readFileSync(fullPath, 'utf-8');
392
- this.fileCache.set(filePath, content);
393
- return content;
394
- }
395
- catch (error) {
396
- (0, errors_1.logDebug)('Failed to read file for resolution', { filePath, error: String(error) });
397
- this.fileCache.set(filePath, null);
398
- return null;
399
- }
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;
400
459
  },
401
460
  getProjectRoot: () => this.projectRoot,
402
461
  getAllFiles: () => {
@@ -499,7 +558,7 @@ class ReferenceResolver {
499
558
  // `.ts` index barrel and silently break the chain (#629). Re-key
500
559
  // the parse on the barrel's extension so the chase works no matter
501
560
  // what kind of file imports through it.
502
- const isJsFamily = /\.(?:d\.ts|[cm]?tsx?|[cm]?jsx?)$/i.test(filePath);
561
+ const isJsFamily = /\.(?:d\.ts|[cm]?tsx?|[cm]?jsx?|ets)$/i.test(filePath);
503
562
  const reExports = (0, import_resolver_1.extractReExports)(content, isJsFamily ? 'typescript' : language);
504
563
  this.reExportCache.set(filePath, reExports);
505
564
  return reExports;
@@ -614,6 +673,24 @@ class ReferenceResolver {
614
673
  return true;
615
674
  }
616
675
  }
676
+ // Lua/Luau method calls use a single `:` (`lg:log`); R uses `$` (`lg$log`).
677
+ // Check the member (and receiver) around these separators too, so the ref
678
+ // isn't dropped here before the method-call resolver ever sees it. The `:`
679
+ // case is skipped when the name actually contains `::` (handled above).
680
+ for (const sep of [':', '$']) {
681
+ if (sep === ':' && name.includes('::'))
682
+ continue;
683
+ const sepIdx = name.indexOf(sep);
684
+ if (sepIdx > 0) {
685
+ const receiver = name.substring(0, sepIdx);
686
+ const member = name.substring(sepIdx + 1);
687
+ if (this.knownNames.has(member) || this.knownNames.has(receiver))
688
+ return true;
689
+ const capitalized = receiver.charAt(0).toUpperCase() + receiver.slice(1);
690
+ if (this.knownNames.has(capitalized))
691
+ return true;
692
+ }
693
+ }
617
694
  // For path-like references (e.g., "snippets/drawer-menu.liquid"), check the filename
618
695
  const slashIdx = name.lastIndexOf('/');
619
696
  if (slashIdx > 0) {
@@ -648,13 +725,36 @@ class ReferenceResolver {
648
725
  if (this.isBuiltInOrExternal(ref)) {
649
726
  return null;
650
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
+ }
651
742
  // Fast pre-filter: skip if no symbol with this name exists anywhere
652
743
  // AND the name doesn't match a local import. The import escape is
653
744
  // necessary because re-export rename chains (`import { login }
654
745
  // from './barrel'` where the barrel has `export { signIn as login }
655
746
  // from './auth'`) intentionally call a name that has no
656
747
  // declaration anywhere — only the renamed upstream symbol does.
657
- 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) &&
658
758
  !this.matchesAnyImport(ref) &&
659
759
  !this.frameworks.some((f) => f.claimsReference?.(ref.referenceName))) {
660
760
  return null;
@@ -720,13 +820,40 @@ class ReferenceResolver {
720
820
  // If that didn't find the file, do NOT fall back to the symbol
721
821
  // name-matcher — it would mis-connect e.g. "inc/db.php" to an unrelated
722
822
  // db.php elsewhere in the tree (a wrong edge is worse than none, #660).
723
- 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') {
724
830
  return candidates.length > 0
725
831
  ? candidates.reduce((best, curr) => curr.confidence > best.confidence ? curr : best)
726
832
  : null;
727
833
  }
728
834
  // Strategy 3: Try name matching
729
- 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
+ }
730
857
  if (nameResult) {
731
858
  candidates.push(nameResult);
732
859
  }
@@ -813,6 +940,20 @@ class ReferenceResolver {
813
940
  referenceKind: r.original.referenceKind,
814
941
  })));
815
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
+ }
816
957
  return result;
817
958
  }
818
959
  /**
@@ -828,7 +969,7 @@ class ReferenceResolver {
828
969
  * (re-resolving an already-resolved ref is a no-op since it's been deleted).
829
970
  * Returns the number of newly-created edges.
830
971
  */
831
- resolveChainedCallsViaConformance() {
972
+ async resolveChainedCallsViaConformance() {
832
973
  const deferred = this.deferredChainRefs;
833
974
  this.deferredChainRefs = [];
834
975
  if (deferred.length === 0)
@@ -837,6 +978,10 @@ class ReferenceResolver {
837
978
  // these refs were deferred). matchDottedCallChain now resolves a method on a
838
979
  // supertype via context.getSupertypes -> resolveMethodOnType's conformance walk.
839
980
  this.clearCaches();
981
+ // This post-pass runs synchronously on the indexer's main thread; yield
982
+ // periodically so the #850 liveness watchdog heartbeat can fire on a repo
983
+ // with many deferred chained calls (#1091).
984
+ const maybeYield = (0, cooperative_yield_1.createYielder)();
840
985
  const resolved = [];
841
986
  for (const ref of deferred) {
842
987
  // `::`-receiver languages (Rust) split on `::` (matchScopedCallChain);
@@ -847,6 +992,7 @@ class ReferenceResolver {
847
992
  const match = this.gateLanguage(chainMatch, ref);
848
993
  if (match)
849
994
  resolved.push(match);
995
+ await maybeYield();
850
996
  }
851
997
  if (resolved.length === 0)
852
998
  return 0;
@@ -857,6 +1003,55 @@ class ReferenceResolver {
857
1003
  }
858
1004
  return edges.length;
859
1005
  }
1006
+ /**
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.
1018
+ */
1019
+ async resolveBatchYielding(batch, maybeYield) {
1020
+ this.warmCaches();
1021
+ const resolved = [];
1022
+ const unresolved = [];
1023
+ const byMethod = {};
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);
1041
+ }
1042
+ await maybeYield();
1043
+ }
1044
+ return {
1045
+ resolved,
1046
+ unresolved,
1047
+ stats: {
1048
+ total: batch.length,
1049
+ resolved: resolved.length,
1050
+ unresolved: unresolved.length,
1051
+ byMethod,
1052
+ },
1053
+ };
1054
+ }
860
1055
  /**
861
1056
  * Resolve and persist in batches to keep memory bounded.
862
1057
  * Processes unresolved references in chunks, persisting edges and cleaning
@@ -864,6 +1059,13 @@ class ReferenceResolver {
864
1059
  */
865
1060
  async resolveAndPersistBatched(onProgress, batchSize = 5000) {
866
1061
  this.warmCaches();
1062
+ // Resolution runs on the indexer's MAIN thread, and the #850 liveness
1063
+ // watchdog SIGKILLs a process whose event loop stalls past its window (60s
1064
+ // by default). A single dense batch's resolveAll — or the synthesis pass
1065
+ // below — can exceed that on a large repo, killing a VALID in-progress index
1066
+ // (#1091). A shared yielder lets both give the watchdog heartbeat a regular
1067
+ // window to fire; see ./cooperative-yield.
1068
+ const maybeYield = (0, cooperative_yield_1.createYielder)();
867
1069
  const total = this.queries.getUnresolvedReferencesCount();
868
1070
  let processed = 0;
869
1071
  const aggregateStats = {
@@ -879,7 +1081,7 @@ class ReferenceResolver {
879
1081
  const batch = this.queries.getUnresolvedReferencesBatch(0, batchSize);
880
1082
  if (batch.length === 0)
881
1083
  break;
882
- const result = this.resolveAll(batch);
1084
+ const result = await this.resolveBatchYielding(batch, maybeYield);
883
1085
  // Persist edges immediately
884
1086
  const edges = this.createEdges(result.resolved);
885
1087
  if (edges.length > 0) {
@@ -912,11 +1114,13 @@ class ReferenceResolver {
912
1114
  onProgress?.(processed, total);
913
1115
  // Yield so progress UI can render between batches
914
1116
  await new Promise(resolve => setImmediate(resolve));
915
- // If nothing was resolved or removed in this batch, we'd loop forever
916
- // on the same rows. Break to avoid infinite loop.
917
- if (result.resolved.length === 0 && result.unresolved.length === batch.length) {
918
- break;
919
- }
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.
920
1124
  // Non-progress guard (defense-in-depth). Because we re-read from offset 0
921
1125
  // each pass, the unresolved_refs table MUST shrink every iteration — both
922
1126
  // resolved and unresolved refs are deleted above. If it didn't shrink, a
@@ -935,7 +1139,7 @@ class ReferenceResolver {
935
1139
  // callbacks) that static parsing leaves out. Best-effort — never fail the
936
1140
  // index on it. See docs/design/callback-edge-synthesis.md.
937
1141
  try {
938
- aggregateStats.byMethod['callback-synthesis'] = (0, callback_synthesizer_1.synthesizeCallbackEdges)(this.queries, this.context);
1142
+ aggregateStats.byMethod['callback-synthesis'] = await (0, callback_synthesizer_1.synthesizeCallbackEdges)(this.queries, this.context);
939
1143
  }
940
1144
  catch {
941
1145
  // synthesis is additive and optional; ignore failures
@@ -958,11 +1162,18 @@ class ReferenceResolver {
958
1162
  isBuiltInOrExternal(ref) {
959
1163
  const name = ref.referenceName;
960
1164
  const isJsTs = ref.language === 'typescript' || ref.language === 'javascript'
961
- || ref.language === 'tsx' || ref.language === 'jsx';
1165
+ || ref.language === 'tsx' || ref.language === 'jsx' || ref.language === 'arkts';
962
1166
  // JavaScript/TypeScript built-ins
963
1167
  if (isJsTs && JS_BUILT_INS.has(name)) {
964
1168
  return true;
965
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
+ }
966
1177
  // Common JS/TS library calls (console.log, Math.floor, JSON.parse)
967
1178
  if (isJsTs && (name.startsWith('console.') || name.startsWith('Math.') || name.startsWith('JSON.'))) {
968
1179
  return true;
@@ -1127,6 +1338,96 @@ class ReferenceResolver {
1127
1338
  const target = found.values().next().value;
1128
1339
  return { original: ref, targetNodeId: target.id, confidence: 0.9, resolvedBy: 'import' };
1129
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
+ }
1130
1431
  /**
1131
1432
  * Resolve a `this.<member>` function-as-value reference (#756/#808) to the
1132
1433
  * ENCLOSING CLASS's own member — never a same-named symbol elsewhere. The
@@ -1186,14 +1487,18 @@ class ReferenceResolver {
1186
1487
  * Mirrors resolveChainedCallsViaConformance's lifecycle. Returns the number
1187
1488
  * of newly-created edges.
1188
1489
  */
1189
- resolveDeferredThisMemberRefs() {
1490
+ async resolveDeferredThisMemberRefs() {
1190
1491
  const deferred = this.deferredThisMemberRefs;
1191
1492
  this.deferredThisMemberRefs = [];
1192
1493
  if (deferred.length === 0)
1193
1494
  return 0;
1194
1495
  this.clearCaches();
1496
+ // Synchronous main-thread post-pass with a per-ref supertype BFS — yield
1497
+ // periodically so the #850 liveness watchdog heartbeat can fire (#1091).
1498
+ const maybeYield = (0, cooperative_yield_1.createYielder)();
1195
1499
  const resolved = [];
1196
1500
  for (const ref of deferred) {
1501
+ await maybeYield();
1197
1502
  const member = ref.referenceName.slice('this.'.length);
1198
1503
  const fromNode = this.queries.getNodeById(ref.fromNodeId);
1199
1504
  if (!fromNode || !member)