homegraph 1.1.3 → 1.4.1

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 (412) hide show
  1. package/README.md +43 -18
  2. package/dist/arkts/ohos-api-index.d.ts +15 -0
  3. package/dist/arkts/ohos-api-index.d.ts.map +1 -0
  4. package/dist/arkts/ohos-api-index.js +190 -0
  5. package/dist/arkts/ohos-api-index.js.map +1 -0
  6. package/dist/arkts/ohos-sdk-input.d.ts +36 -0
  7. package/dist/arkts/ohos-sdk-input.d.ts.map +1 -0
  8. package/dist/arkts/ohos-sdk-input.js +214 -0
  9. package/dist/arkts/ohos-sdk-input.js.map +1 -0
  10. package/dist/bin/command-supervision.d.ts +12 -0
  11. package/dist/bin/command-supervision.d.ts.map +1 -0
  12. package/dist/bin/command-supervision.js +86 -0
  13. package/dist/bin/command-supervision.js.map +1 -0
  14. package/dist/bin/homegraph.d.ts +1 -1
  15. package/dist/bin/homegraph.js +709 -168
  16. package/dist/bin/homegraph.js.map +1 -1
  17. package/dist/context/index.d.ts.map +1 -1
  18. package/dist/context/index.js +85 -0
  19. package/dist/context/index.js.map +1 -1
  20. package/dist/db/index.d.ts +84 -5
  21. package/dist/db/index.d.ts.map +1 -1
  22. package/dist/db/index.js +217 -14
  23. package/dist/db/index.js.map +1 -1
  24. package/dist/db/migrations.d.ts +1 -1
  25. package/dist/db/migrations.d.ts.map +1 -1
  26. package/dist/db/migrations.js +58 -1
  27. package/dist/db/migrations.js.map +1 -1
  28. package/dist/db/queries.d.ts +131 -2
  29. package/dist/db/queries.d.ts.map +1 -1
  30. package/dist/db/queries.js +473 -9
  31. package/dist/db/queries.js.map +1 -1
  32. package/dist/db/schema.sql +52 -1
  33. package/dist/db/wal-valve.d.ts +106 -0
  34. package/dist/db/wal-valve.d.ts.map +1 -0
  35. package/dist/db/wal-valve.js +208 -0
  36. package/dist/db/wal-valve.js.map +1 -0
  37. package/dist/directory.d.ts +36 -0
  38. package/dist/directory.d.ts.map +1 -1
  39. package/dist/directory.js +274 -0
  40. package/dist/directory.js.map +1 -1
  41. package/dist/extraction/arkts-batch-worker.d.ts +2 -0
  42. package/dist/extraction/arkts-batch-worker.d.ts.map +1 -0
  43. package/dist/extraction/arkts-batch-worker.js +28 -0
  44. package/dist/extraction/arkts-batch-worker.js.map +1 -0
  45. package/dist/extraction/cfml-extractor.d.ts +107 -0
  46. package/dist/extraction/cfml-extractor.d.ts.map +1 -0
  47. package/dist/extraction/cfml-extractor.js +494 -0
  48. package/dist/extraction/cfml-extractor.js.map +1 -0
  49. package/dist/extraction/context.d.ts +11 -0
  50. package/dist/extraction/context.d.ts.map +1 -1
  51. package/dist/extraction/context.js +20 -0
  52. package/dist/extraction/context.js.map +1 -1
  53. package/dist/extraction/grammars.d.ts +25 -1
  54. package/dist/extraction/grammars.d.ts.map +1 -1
  55. package/dist/extraction/grammars.js +177 -24
  56. package/dist/extraction/grammars.js.map +1 -1
  57. package/dist/extraction/index.d.ts +55 -2
  58. package/dist/extraction/index.d.ts.map +1 -1
  59. package/dist/extraction/index.js +923 -381
  60. package/dist/extraction/index.js.map +1 -1
  61. package/dist/extraction/languages/arkts-state-decorators.d.ts +13 -0
  62. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +1 -0
  63. package/dist/extraction/languages/arkts-state-decorators.js +26 -0
  64. package/dist/extraction/languages/arkts-state-decorators.js.map +1 -0
  65. package/dist/extraction/languages/arkts-viewtree.d.ts +4 -2
  66. package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
  67. package/dist/extraction/languages/arkts-viewtree.js +21 -6
  68. package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
  69. package/dist/extraction/languages/arkts.d.ts +98 -2
  70. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  71. package/dist/extraction/languages/arkts.js +1290 -61
  72. package/dist/extraction/languages/arkts.js.map +1 -1
  73. package/dist/extraction/languages/c-cpp.d.ts +98 -0
  74. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  75. package/dist/extraction/languages/c-cpp.js +563 -1
  76. package/dist/extraction/languages/c-cpp.js.map +1 -1
  77. package/dist/extraction/languages/cfquery.d.ts +12 -0
  78. package/dist/extraction/languages/cfquery.d.ts.map +1 -0
  79. package/dist/extraction/languages/cfquery.js +28 -0
  80. package/dist/extraction/languages/cfquery.js.map +1 -0
  81. package/dist/extraction/languages/cfscript.d.ts +3 -0
  82. package/dist/extraction/languages/cfscript.d.ts.map +1 -0
  83. package/dist/extraction/languages/cfscript.js +73 -0
  84. package/dist/extraction/languages/cfscript.js.map +1 -0
  85. package/dist/extraction/languages/cobol.d.ts +33 -0
  86. package/dist/extraction/languages/cobol.d.ts.map +1 -0
  87. package/dist/extraction/languages/cobol.js +499 -0
  88. package/dist/extraction/languages/cobol.js.map +1 -0
  89. package/dist/extraction/languages/erlang.d.ts +3 -0
  90. package/dist/extraction/languages/erlang.d.ts.map +1 -0
  91. package/dist/extraction/languages/erlang.js +350 -0
  92. package/dist/extraction/languages/erlang.js.map +1 -0
  93. package/dist/extraction/languages/index.d.ts.map +1 -1
  94. package/dist/extraction/languages/index.js +16 -0
  95. package/dist/extraction/languages/index.js.map +1 -1
  96. package/dist/extraction/languages/nix.d.ts +3 -0
  97. package/dist/extraction/languages/nix.d.ts.map +1 -0
  98. package/dist/extraction/languages/nix.js +294 -0
  99. package/dist/extraction/languages/nix.js.map +1 -0
  100. package/dist/extraction/languages/ohos-api-consumer.d.ts +34 -0
  101. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +1 -0
  102. package/dist/extraction/languages/ohos-api-consumer.js +283 -0
  103. package/dist/extraction/languages/ohos-api-consumer.js.map +1 -0
  104. package/dist/extraction/languages/solidity.d.ts +3 -0
  105. package/dist/extraction/languages/solidity.d.ts.map +1 -0
  106. package/dist/extraction/languages/solidity.js +293 -0
  107. package/dist/extraction/languages/solidity.js.map +1 -0
  108. package/dist/extraction/languages/terraform.d.ts +3 -0
  109. package/dist/extraction/languages/terraform.d.ts.map +1 -0
  110. package/dist/extraction/languages/terraform.js +641 -0
  111. package/dist/extraction/languages/terraform.js.map +1 -0
  112. package/dist/extraction/languages/vbnet.d.ts +11 -0
  113. package/dist/extraction/languages/vbnet.d.ts.map +1 -0
  114. package/dist/extraction/languages/vbnet.js +141 -0
  115. package/dist/extraction/languages/vbnet.js.map +1 -0
  116. package/dist/extraction/mybatis-extractor.d.ts +30 -10
  117. package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  118. package/dist/extraction/mybatis-extractor.js +140 -38
  119. package/dist/extraction/mybatis-extractor.js.map +1 -1
  120. package/dist/extraction/parse-pool.d.ts +152 -0
  121. package/dist/extraction/parse-pool.d.ts.map +1 -0
  122. package/dist/extraction/parse-pool.js +387 -0
  123. package/dist/extraction/parse-pool.js.map +1 -0
  124. package/dist/extraction/parse-worker.js +9 -2
  125. package/dist/extraction/parse-worker.js.map +1 -1
  126. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  127. package/dist/extraction/tree-sitter-helpers.js +1 -0
  128. package/dist/extraction/tree-sitter-helpers.js.map +1 -1
  129. package/dist/extraction/tree-sitter-types.d.ts +20 -1
  130. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  131. package/dist/extraction/tree-sitter.d.ts +59 -0
  132. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  133. package/dist/extraction/tree-sitter.js +952 -37
  134. package/dist/extraction/tree-sitter.js.map +1 -1
  135. package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  136. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  137. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  138. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  139. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  140. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  141. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  142. package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  143. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  144. package/dist/graph/traversal.d.ts.map +1 -1
  145. package/dist/graph/traversal.js +77 -18
  146. package/dist/graph/traversal.js.map +1 -1
  147. package/dist/index.d.ts +85 -2
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +426 -9
  150. package/dist/index.js.map +1 -1
  151. package/dist/installer/index.d.ts +42 -0
  152. package/dist/installer/index.d.ts.map +1 -1
  153. package/dist/installer/index.js +97 -2
  154. package/dist/installer/index.js.map +1 -1
  155. package/dist/mcp/daemon-paths.d.ts +30 -3
  156. package/dist/mcp/daemon-paths.d.ts.map +1 -1
  157. package/dist/mcp/daemon-paths.js +50 -10
  158. package/dist/mcp/daemon-paths.js.map +1 -1
  159. package/dist/mcp/daemon-registry.d.ts.map +1 -1
  160. package/dist/mcp/daemon-registry.js +7 -3
  161. package/dist/mcp/daemon-registry.js.map +1 -1
  162. package/dist/mcp/daemon.d.ts +73 -3
  163. package/dist/mcp/daemon.d.ts.map +1 -1
  164. package/dist/mcp/daemon.js +258 -40
  165. package/dist/mcp/daemon.js.map +1 -1
  166. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  167. package/dist/mcp/dynamic-boundaries.js +2 -1
  168. package/dist/mcp/dynamic-boundaries.js.map +1 -1
  169. package/dist/mcp/early-ppid.d.ts +26 -0
  170. package/dist/mcp/early-ppid.d.ts.map +1 -0
  171. package/dist/mcp/early-ppid.js +29 -0
  172. package/dist/mcp/early-ppid.js.map +1 -0
  173. package/dist/mcp/engine.d.ts +16 -0
  174. package/dist/mcp/engine.d.ts.map +1 -1
  175. package/dist/mcp/engine.js +100 -2
  176. package/dist/mcp/engine.js.map +1 -1
  177. package/dist/mcp/index.d.ts.map +1 -1
  178. package/dist/mcp/index.js +66 -49
  179. package/dist/mcp/index.js.map +1 -1
  180. package/dist/mcp/liveness-watchdog.d.ts +18 -1
  181. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  182. package/dist/mcp/liveness-watchdog.js +73 -13
  183. package/dist/mcp/liveness-watchdog.js.map +1 -1
  184. package/dist/mcp/memory-budget.d.ts +30 -0
  185. package/dist/mcp/memory-budget.d.ts.map +1 -0
  186. package/dist/mcp/memory-budget.js +123 -0
  187. package/dist/mcp/memory-budget.js.map +1 -0
  188. package/dist/mcp/ppid-watchdog.d.ts +18 -0
  189. package/dist/mcp/ppid-watchdog.d.ts.map +1 -1
  190. package/dist/mcp/ppid-watchdog.js +37 -0
  191. package/dist/mcp/ppid-watchdog.js.map +1 -1
  192. package/dist/mcp/proxy.d.ts.map +1 -1
  193. package/dist/mcp/proxy.js +88 -4
  194. package/dist/mcp/proxy.js.map +1 -1
  195. package/dist/mcp/query-cache.d.ts +56 -0
  196. package/dist/mcp/query-cache.d.ts.map +1 -0
  197. package/dist/mcp/query-cache.js +261 -0
  198. package/dist/mcp/query-cache.js.map +1 -0
  199. package/dist/mcp/query-pool.d.ts +128 -0
  200. package/dist/mcp/query-pool.d.ts.map +1 -0
  201. package/dist/mcp/query-pool.js +374 -0
  202. package/dist/mcp/query-pool.js.map +1 -0
  203. package/dist/mcp/query-worker.d.ts +24 -0
  204. package/dist/mcp/query-worker.d.ts.map +1 -0
  205. package/dist/mcp/query-worker.js +87 -0
  206. package/dist/mcp/query-worker.js.map +1 -0
  207. package/dist/mcp/server-instructions.d.ts +4 -29
  208. package/dist/mcp/server-instructions.d.ts.map +1 -1
  209. package/dist/mcp/server-instructions.js +39 -86
  210. package/dist/mcp/server-instructions.js.map +1 -1
  211. package/dist/mcp/session.d.ts +14 -0
  212. package/dist/mcp/session.d.ts.map +1 -1
  213. package/dist/mcp/session.js +28 -1
  214. package/dist/mcp/session.js.map +1 -1
  215. package/dist/mcp/startup-handshake.d.ts +44 -0
  216. package/dist/mcp/startup-handshake.d.ts.map +1 -0
  217. package/dist/mcp/startup-handshake.js +73 -0
  218. package/dist/mcp/startup-handshake.js.map +1 -0
  219. package/dist/mcp/tools.d.ts +179 -2
  220. package/dist/mcp/tools.d.ts.map +1 -1
  221. package/dist/mcp/tools.js +3027 -192
  222. package/dist/mcp/tools.js.map +1 -1
  223. package/dist/mcp/transport.d.ts.map +1 -1
  224. package/dist/mcp/transport.js +18 -0
  225. package/dist/mcp/transport.js.map +1 -1
  226. package/dist/project-config.d.ts +58 -0
  227. package/dist/project-config.d.ts.map +1 -1
  228. package/dist/project-config.js +141 -2
  229. package/dist/project-config.js.map +1 -1
  230. package/dist/reasoning/login.js +1 -1
  231. package/dist/reasoning/login.js.map +1 -1
  232. package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -29
  233. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  234. package/dist/resolution/c-fnptr-synthesizer.js +866 -148
  235. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  236. package/dist/resolution/callback-synthesizer.d.ts +9 -1
  237. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  238. package/dist/resolution/callback-synthesizer.js +1232 -183
  239. package/dist/resolution/callback-synthesizer.js.map +1 -1
  240. package/dist/resolution/cooperative-yield.d.ts +32 -0
  241. package/dist/resolution/cooperative-yield.d.ts.map +1 -0
  242. package/dist/resolution/cooperative-yield.js +42 -0
  243. package/dist/resolution/cooperative-yield.js.map +1 -0
  244. package/dist/resolution/frameworks/cics.d.ts +20 -0
  245. package/dist/resolution/frameworks/cics.d.ts.map +1 -0
  246. package/dist/resolution/frameworks/cics.js +90 -0
  247. package/dist/resolution/frameworks/cics.js.map +1 -0
  248. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  249. package/dist/resolution/frameworks/index.js +3 -0
  250. package/dist/resolution/frameworks/index.js.map +1 -1
  251. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  252. package/dist/resolution/frameworks/java.js +14 -6
  253. package/dist/resolution/frameworks/java.js.map +1 -1
  254. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  255. package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  256. package/dist/resolution/frameworks/terraform.js +277 -0
  257. package/dist/resolution/frameworks/terraform.js.map +1 -0
  258. package/dist/resolution/goframe-synthesizer.d.ts +2 -1
  259. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
  260. package/dist/resolution/goframe-synthesizer.js +8 -3
  261. package/dist/resolution/goframe-synthesizer.js.map +1 -1
  262. package/dist/resolution/import-resolver.d.ts +7 -0
  263. package/dist/resolution/import-resolver.d.ts.map +1 -1
  264. package/dist/resolution/import-resolver.js +129 -4
  265. package/dist/resolution/import-resolver.js.map +1 -1
  266. package/dist/resolution/index.d.ts +65 -3
  267. package/dist/resolution/index.d.ts.map +1 -1
  268. package/dist/resolution/index.js +477 -59
  269. package/dist/resolution/index.js.map +1 -1
  270. package/dist/resolution/name-matcher.d.ts +22 -3
  271. package/dist/resolution/name-matcher.d.ts.map +1 -1
  272. package/dist/resolution/name-matcher.js +657 -39
  273. package/dist/resolution/name-matcher.js.map +1 -1
  274. package/dist/resolution/strip-comments.d.ts +1 -1
  275. package/dist/resolution/strip-comments.d.ts.map +1 -1
  276. package/dist/resolution/strip-comments.js +49 -0
  277. package/dist/resolution/strip-comments.js.map +1 -1
  278. package/dist/resolution/types.d.ts +29 -0
  279. package/dist/resolution/types.d.ts.map +1 -1
  280. package/dist/resolution/workspace-packages.d.ts +10 -0
  281. package/dist/resolution/workspace-packages.d.ts.map +1 -1
  282. package/dist/resolution/workspace-packages.js +142 -4
  283. package/dist/resolution/workspace-packages.js.map +1 -1
  284. package/dist/search/identifier-segments.d.ts +60 -0
  285. package/dist/search/identifier-segments.d.ts.map +1 -0
  286. package/dist/search/identifier-segments.js +176 -0
  287. package/dist/search/identifier-segments.js.map +1 -0
  288. package/dist/search/query-utils.d.ts +201 -0
  289. package/dist/search/query-utils.d.ts.map +1 -1
  290. package/dist/search/query-utils.js +919 -2
  291. package/dist/search/query-utils.js.map +1 -1
  292. package/dist/spec/config.d.ts +7 -11
  293. package/dist/spec/config.d.ts.map +1 -1
  294. package/dist/spec/config.js +77 -48
  295. package/dist/spec/config.js.map +1 -1
  296. package/dist/spec/db/fragment-node.d.ts +1 -0
  297. package/dist/spec/db/fragment-node.d.ts.map +1 -1
  298. package/dist/spec/db/fragment-node.js +8 -0
  299. package/dist/spec/db/fragment-node.js.map +1 -1
  300. package/dist/spec/db/fts.d.ts +14 -0
  301. package/dist/spec/db/fts.d.ts.map +1 -1
  302. package/dist/spec/db/fts.js +43 -4
  303. package/dist/spec/db/fts.js.map +1 -1
  304. package/dist/spec/db/schema.d.ts +6 -3
  305. package/dist/spec/db/schema.d.ts.map +1 -1
  306. package/dist/spec/db/schema.js +36 -4
  307. package/dist/spec/db/schema.js.map +1 -1
  308. package/dist/spec/db/schema.sql +11 -1
  309. package/dist/spec/evolve/logic-checker.d.ts +2 -2
  310. package/dist/spec/evolve/logic-checker.d.ts.map +1 -1
  311. package/dist/spec/evolve/logic-checker.js +3 -27
  312. package/dist/spec/evolve/logic-checker.js.map +1 -1
  313. package/dist/spec/evolve/pipeline.d.ts +21 -19
  314. package/dist/spec/evolve/pipeline.d.ts.map +1 -1
  315. package/dist/spec/evolve/pipeline.js +284 -31
  316. package/dist/spec/evolve/pipeline.js.map +1 -1
  317. package/dist/spec/evolve/spec-rewriter.d.ts +2 -2
  318. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
  319. package/dist/spec/evolve/spec-rewriter.js +7 -31
  320. package/dist/spec/evolve/spec-rewriter.js.map +1 -1
  321. package/dist/spec/graph/queries.d.ts +97 -0
  322. package/dist/spec/graph/queries.d.ts.map +1 -1
  323. package/dist/spec/graph/queries.js +225 -0
  324. package/dist/spec/graph/queries.js.map +1 -1
  325. package/dist/spec/llm/client.d.ts +29 -0
  326. package/dist/spec/llm/client.d.ts.map +1 -0
  327. package/dist/spec/llm/client.js +123 -0
  328. package/dist/spec/llm/client.js.map +1 -0
  329. package/dist/spec/llm/index.d.ts +3 -0
  330. package/dist/spec/llm/index.d.ts.map +1 -0
  331. package/dist/spec/llm/index.js +11 -0
  332. package/dist/spec/llm/index.js.map +1 -0
  333. package/dist/spec/llm/prompts.d.ts +13 -0
  334. package/dist/spec/llm/prompts.d.ts.map +1 -0
  335. package/dist/spec/llm/prompts.js +75 -0
  336. package/dist/spec/llm/prompts.js.map +1 -0
  337. package/dist/spec/mining/git-scanner.d.ts +12 -0
  338. package/dist/spec/mining/git-scanner.d.ts.map +1 -1
  339. package/dist/spec/mining/git-scanner.js +41 -0
  340. package/dist/spec/mining/git-scanner.js.map +1 -1
  341. package/dist/spec/mining/pipeline.d.ts.map +1 -1
  342. package/dist/spec/mining/pipeline.js +14 -1
  343. package/dist/spec/mining/pipeline.js.map +1 -1
  344. package/dist/spec/utils.d.ts +2 -1
  345. package/dist/spec/utils.d.ts.map +1 -1
  346. package/dist/spec/utils.js +3 -1
  347. package/dist/spec/utils.js.map +1 -1
  348. package/dist/sync/git-hooks.d.ts.map +1 -1
  349. package/dist/sync/git-hooks.js +2 -0
  350. package/dist/sync/git-hooks.js.map +1 -1
  351. package/dist/sync/watcher.d.ts +10 -5
  352. package/dist/sync/watcher.d.ts.map +1 -1
  353. package/dist/sync/watcher.js +51 -14
  354. package/dist/sync/watcher.js.map +1 -1
  355. package/dist/sync/worktree.d.ts +9 -0
  356. package/dist/sync/worktree.d.ts.map +1 -1
  357. package/dist/sync/worktree.js +45 -0
  358. package/dist/sync/worktree.js.map +1 -1
  359. package/dist/types.d.ts +25 -2
  360. package/dist/types.d.ts.map +1 -1
  361. package/dist/types.js +10 -0
  362. package/dist/types.js.map +1 -1
  363. package/dist/ui/shimmer-progress.d.ts +2 -0
  364. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  365. package/dist/ui/shimmer-progress.js +19 -2
  366. package/dist/ui/shimmer-progress.js.map +1 -1
  367. package/dist/upgrade/index.d.ts +32 -0
  368. package/dist/upgrade/index.d.ts.map +1 -1
  369. package/dist/upgrade/index.js +163 -15
  370. package/dist/upgrade/index.js.map +1 -1
  371. package/dist/upgrade/remove-binary.d.ts +87 -0
  372. package/dist/upgrade/remove-binary.d.ts.map +1 -0
  373. package/dist/upgrade/remove-binary.js +289 -0
  374. package/dist/upgrade/remove-binary.js.map +1 -0
  375. package/dist/upgrade/update-check.d.ts +92 -0
  376. package/dist/upgrade/update-check.d.ts.map +1 -0
  377. package/dist/upgrade/update-check.js +258 -0
  378. package/dist/upgrade/update-check.js.map +1 -0
  379. package/package.json +4 -4
  380. package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
  381. package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
  382. package/scripts/agent-eval/run-all.sh +6 -0
  383. package/scripts/build-bundle.sh +5 -0
  384. package/scripts/exp_boundary_eval/README.md +247 -0
  385. package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -0
  386. package/scripts/exp_boundary_eval/_test_stdin.py +8 -0
  387. package/scripts/exp_boundary_eval/_utils.py +1116 -0
  388. package/scripts/exp_boundary_eval/analyze.py +1313 -0
  389. package/scripts/exp_boundary_eval/deveco_arm.py +519 -0
  390. package/scripts/exp_boundary_eval/run_all.py +378 -0
  391. package/scripts/exp_boundary_eval/run_one.py +165 -0
  392. package/scripts/exp_boundary_eval/run_session.py +158 -0
  393. package/scripts/exp_boundary_eval/setup.py +120 -0
  394. package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -0
  395. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -0
  396. package/scripts/exp_boundary_eval/win_node_launcher.py +24 -0
  397. package/scripts/npm-shim.js +32 -3
  398. package/scripts/ohos-sdk-publish.mjs +133 -0
  399. package/scripts/qa_eval/README.md +0 -407
  400. package/scripts/qa_eval/_test_deveco_probe.py +0 -41
  401. package/scripts/qa_eval/agent_runner.py +0 -526
  402. package/scripts/qa_eval/data/.gitignore +0 -4
  403. package/scripts/qa_eval/data/test-set.jsonl +0 -2
  404. package/scripts/qa_eval/eval_metrics.py +0 -274
  405. package/scripts/qa_eval/external_agent.py +0 -976
  406. package/scripts/qa_eval/llm_config.py +0 -92
  407. package/scripts/qa_eval/memory_monitor.py +0 -132
  408. package/scripts/qa_eval/my_answer_accuracy.py +0 -187
  409. package/scripts/qa_eval/requirements.txt +0 -2
  410. package/scripts/qa_eval/run_pipeline.py +0 -804
  411. package/scripts/qa_eval/stats_efficiency.py +0 -279
  412. package/scripts/qa_eval/stats_scores.py +0 -207
@@ -46,6 +46,7 @@ const tree_sitter_helpers_1 = require("./tree-sitter-helpers");
46
46
  const function_ref_1 = require("./function-ref");
47
47
  const generated_detection_1 = require("./generated-detection");
48
48
  const languages_1 = require("./languages");
49
+ const c_cpp_1 = require("./languages/c-cpp");
49
50
  const liquid_extractor_1 = require("./liquid-extractor");
50
51
  const razor_extractor_1 = require("./razor-extractor");
51
52
  const svelte_extractor_1 = require("./svelte-extractor");
@@ -53,6 +54,7 @@ const astro_extractor_1 = require("./astro-extractor");
53
54
  const dfm_extractor_1 = require("./dfm-extractor");
54
55
  const vue_extractor_1 = require("./vue-extractor");
55
56
  const mybatis_extractor_1 = require("./mybatis-extractor");
57
+ const cfml_extractor_1 = require("./cfml-extractor");
56
58
  const arkts_1 = require("./languages/arkts");
57
59
  const frameworks_1 = require("../resolution/frameworks");
58
60
  // Re-export for backward compatibility
@@ -77,24 +79,68 @@ const VUE_STORE_FACTORY_CALLEES = new Set(['defineStore', 'createStore']);
77
79
  /** Distinct signals that a file is a Vuex/Pinia store (≥2 ⇒ treat a bare
78
80
  * `const actions = {…}` as a store collection — see looksLikeVueStoreFile). */
79
81
  const VUE_STORE_FILE_SIGNAL = /\bdefineStore\b|\bcreateStore\b|\bVuex\b|\bmutations\b|\bactions\b|\bgetters\b|\bnamespaced\b/g;
82
+ /**
83
+ * Erlang calls that take their real callee as (Module, Function, Args)
84
+ * ARGUMENTS — the spawn/apply family. Keys are the callee as the call site
85
+ * spells it: bare for auto-imported BIFs, `module:function` for remote calls.
86
+ * Used by the erlang branch of extractCall to lift a static MFA pair into a
87
+ * call edge (the spawned/applied function is otherwise invisible to the graph).
88
+ */
89
+ /** Compiler-predefined Erlang macros — no `-define` exists to link a use to. */
90
+ const ERLANG_PREDEFINED_MACROS = new Set([
91
+ 'MODULE', 'MODULE_STRING', 'FILE', 'LINE', 'MACHINE',
92
+ 'FUNCTION_NAME', 'FUNCTION_ARITY', 'OTP_RELEASE',
93
+ 'FEATURE_AVAILABLE', 'FEATURE_ENABLED',
94
+ ]);
95
+ const ERLANG_MFA_CALLS = new Set([
96
+ 'spawn', 'spawn_link', 'spawn_monitor', 'spawn_opt', 'apply',
97
+ 'erlang:spawn', 'erlang:spawn_link', 'erlang:spawn_monitor', 'erlang:spawn_opt', 'erlang:apply',
98
+ 'proc_lib:spawn', 'proc_lib:spawn_link', 'proc_lib:spawn_opt', 'proc_lib:start', 'proc_lib:start_link',
99
+ 'timer:apply_after', 'timer:apply_interval',
100
+ 'rpc:call', 'rpc:cast', 'rpc:async_call',
101
+ 'erpc:call', 'erpc:cast',
102
+ ]);
80
103
  /**
81
104
  * Extract the name from a node based on language
82
105
  */
83
106
  function extractName(node, source, extractor) {
107
+ const name = extractNameRaw(node, source, extractor);
108
+ // Universal fallback: recover a real identifier from a name still mangled by a
109
+ // macro the pre-parse didn't blank (C/C++ only — see recoverMangledName). A
110
+ // no-op on well-formed names, so a clean name is never altered.
111
+ return extractor.recoverMangledName ? extractor.recoverMangledName(name) : name;
112
+ }
113
+ function extractNameRaw(node, source, extractor) {
84
114
  const hookName = extractor.resolveName?.(node, source);
85
115
  if (hookName)
86
116
  return hookName;
87
117
  // Try field name first
88
118
  const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, extractor.nameField);
89
119
  if (nameNode) {
90
- // Unwrap pointer_declarator(s) for C/C++ pointer return types
120
+ // Unwrap pointer_declarator / reference_declarator for C/C++ pointer and
121
+ // reference return types (`int* f()`, `int& f()`, `int&& f()`). Without
122
+ // unwrapping the reference wrapper an inline reference-returning method is
123
+ // named "& f() const" instead of "f" — common in Unreal Engine gameplay
124
+ // headers (`const FGameplayTagContainer& GetActiveTags() const`). Out-of-line
125
+ // defs (`T& C::f()`) already resolve via the qualified-name hook. A
126
+ // pointer_declarator exposes its inner through a `declarator` field; a
127
+ // reference_declarator has none, so it's reached via namedChild(0).
91
128
  let resolved = nameNode;
92
- while (resolved.type === 'pointer_declarator') {
129
+ while (resolved.type === 'pointer_declarator' || resolved.type === 'reference_declarator') {
93
130
  const inner = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'declarator') || resolved.namedChild(0);
94
131
  if (!inner)
95
132
  break;
96
133
  resolved = inner;
97
134
  }
135
+ // C++ user-defined conversion operator: the declarator is an `operator_cast`
136
+ // whose first child is the target type and second is the `() const` tail. Name
137
+ // it `operator <type>` (the conventional spelling) rather than the whole
138
+ // `operator EALSMovementState() const` declarator, so it matches symbolic
139
+ // overloads (`operator+`) and is findable by the type name.
140
+ if (resolved.type === 'operator_cast') {
141
+ const typeNode = resolved.namedChild(0);
142
+ return typeNode ? `operator ${(0, tree_sitter_helpers_1.getNodeText)(typeNode, source).trim()}` : (0, tree_sitter_helpers_1.getNodeText)(resolved, source);
143
+ }
98
144
  // Handle complex declarators (C/C++)
99
145
  if (resolved.type === 'function_declarator' || resolved.type === 'declarator') {
100
146
  const innerName = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'declarator') || resolved.namedChild(0);
@@ -324,7 +370,7 @@ class TreeSitterExtractor {
324
370
  // Value-reference edges (default ON; set HOMEGRAPH_VALUE_REFS=0 to disable; see flushValueRefs).
325
371
  // Same-file reads of file-scope const/var symbols → `references` edges so impact analysis catches
326
372
  // value consumers ("change this constant/table, affect its readers").
327
- static VALUE_REF_LANGS = new Set(['typescript', 'javascript', 'tsx', 'go', 'python', 'rust', 'ruby', 'c', 'java', 'csharp', 'php', 'scala', 'kotlin', 'swift', 'dart', 'pascal']);
373
+ static VALUE_REF_LANGS = new Set(['typescript', 'javascript', 'tsx', 'arkts', 'go', 'python', 'rust', 'ruby', 'c', 'java', 'csharp', 'php', 'scala', 'kotlin', 'swift', 'dart', 'pascal']);
328
374
  static MAX_VALUE_REF_NODES = 20_000;
329
375
  valueRefsEnabled = process.env.HOMEGRAPH_VALUE_REFS !== '0';
330
376
  fileScopeValues = new Map();
@@ -333,6 +379,20 @@ class TreeSitterExtractor {
333
379
  errors = [];
334
380
  extractor = null;
335
381
  nodeStack = []; // Stack of parent node IDs
382
+ // C/C++ enclosing `namespace ns { … }` names, prepended to every contained
383
+ // symbol's qualifiedName (see visitNode). Prefix-only by design — no
384
+ // namespace NODE is created: `namespace cutlass {` opens in thousands of
385
+ // files, and a node per block would flood search with same-named symbols
386
+ // (the #1093 crowd-out failure mode). Always empty outside C/C++.
387
+ namespacePrefix = [];
388
+ // C++ local function-pointer bindings, per enclosing symbol:
389
+ // `auto kernel = &flash_fwd_kernel<…>;` recorded as callerId → kernel →
390
+ // {flash_fwd_kernel}, so a later `kernel<<<grid, block>>>(params)` (or plain
391
+ // `kernel(args)`) in the same body emits calls refs to the real target(s)
392
+ // instead of an unresolvable local name. Branch reassignments accumulate —
393
+ // each assigned target is a genuine possible callee. Same-body locality is
394
+ // the precision guard (the #932 table-dispatch philosophy scoped to locals).
395
+ cppLocalFnPtrs = new Map();
336
396
  methodIndex = null; // lookup key → node ID for Pascal defProc lookup
337
397
  // Function-as-value capture (#756): per-language spec + candidates collected
338
398
  // during the walk, gated & flushed into unresolvedReferences at end-of-file
@@ -393,7 +453,7 @@ class TreeSitterExtractor {
393
453
  // this.source so downstream getNodeText reads the same bytes the parser
394
454
  // saw (identical outside the blanked directive lines).
395
455
  if (this.extractor?.preParse) {
396
- this.source = this.extractor.preParse(this.source);
456
+ this.source = this.extractor.preParse(this.source, this.filePath);
397
457
  }
398
458
  this.tree = parser.parse(this.source) ?? null;
399
459
  if (!this.tree) {
@@ -851,6 +911,31 @@ class TreeSitterExtractor {
851
911
  if (skipChildren)
852
912
  return;
853
913
  }
914
+ // C++ namespace blocks: carry the namespace name as a qualifiedName prefix
915
+ // while walking the body, so `namespace flash { void compute_attn(); }`
916
+ // indexes compute_attn with qualifiedName `flash::compute_attn` and a
917
+ // namespace-qualified call (`flash::compute_attn(...)`) resolves by exact
918
+ // qualified match instead of never resolving — C++ namespaces previously
919
+ // left no trace in qualifiedNames at all, so every `ns::fn()` call site
920
+ // was a permanently dead edge (surfaced by #387 flow validation on
921
+ // flash-attention/cutlass, whose kernel dispatch is namespace-qualified).
922
+ // C++17 nested forms (`namespace a::b {`) prefix as written. An anonymous
923
+ // namespace falls through to the generic walk — its contents stay bare,
924
+ // matching how call sites spell them.
925
+ if (this.language === 'cpp' && nodeType === 'namespace_definition') {
926
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
927
+ const nsName = nameNode ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : '';
928
+ if (nsName) {
929
+ this.namespacePrefix.push(nsName);
930
+ for (let i = 0; i < node.namedChildCount; i++) {
931
+ const child = node.namedChild(i);
932
+ if (child)
933
+ this.visitNode(child);
934
+ }
935
+ this.namespacePrefix.pop();
936
+ return;
937
+ }
938
+ }
854
939
  // Function-as-value capture (#756) — independent of the dispatch ladder
855
940
  // below (the captured container types have no other handler there), so it
856
941
  // can never shadow or be shadowed by an extraction branch.
@@ -980,29 +1065,44 @@ class TreeSitterExtractor {
980
1065
  this.scanFnRefSubtree(node, 0);
981
1066
  skipChildren = true; // extractVariable handles children
982
1067
  }
983
- // Swift stored properties inside a type. Swift instance properties aren't
984
- // extracted as their own nodes, but a property's PROPERTY WRAPPER
985
- // (`@Argument`/`@Published`/`@State`/custom) and declared type ARE
986
- // dependencies — attribute them to the enclosing type so the wrapper/type
987
- // files get dependents. Don't skipChildren: an initializer's calls still
988
- // matter. (Other languages extract properties via property/field types.)
1068
+ // Swift properties inside a type. A stored instance property becomes a `field`
1069
+ // node; a `static let`/`static var` member becomes `constant`/`variable`
1070
+ // (Swift's `static`-namespacing idiom — value-reference edges can then target
1071
+ // it); a COMPUTED property (getter block, no stored value) becomes a `property`
1072
+ // node whose getter is walked below so its calls attribute to it. A property's
1073
+ // PROPERTY WRAPPER (`@Argument`/`@Published`/`@State`/custom) and declared type
1074
+ // are dependencies attributed to the enclosing type. (Other languages extract
1075
+ // properties via property/field types.)
989
1076
  else if (this.language === 'swift' &&
990
- nodeType === 'property_declaration' &&
1077
+ (nodeType === 'property_declaration' || nodeType === 'protocol_property_declaration') &&
991
1078
  this.isInsideClassLikeNode()) {
992
1079
  const ownerId = this.nodeStack[this.nodeStack.length - 1];
993
- // A `static let`/`static var` member is a SHARED constant of the type
994
- // (Swift's `static`-namespacing idiom, esp. in `enum`/`struct`) — extract
995
- // it as `constant`/`variable` so value-reference edges can target it. An
996
- // instance stored property stays a `field` (per-instance; Swift instance
997
- // properties otherwise aren't own nodes — that's unchanged). A *computed*
998
- // property (getter, no stored value) is never a constant — skip the node.
999
1080
  const { nameNode, isLet, isComputed } = swiftPropertyInfo(node, this.source);
1000
- if (nameNode && !isComputed) {
1001
- const isStatic = this.extractor.isStatic?.(node) ?? false;
1002
- this.createNode(isStatic ? (isLet ? 'constant' : 'variable') : 'field', (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source), node, {
1003
- visibility: this.extractor.getVisibility?.(node),
1004
- isStatic,
1005
- });
1081
+ let computedPropId;
1082
+ if (nameNode) {
1083
+ if (isComputed) {
1084
+ // Computed property — accessed like a property but its getter holds real
1085
+ // logic. Index as `property` so search/explore find it (#1020: computed
1086
+ // props such as a heavily-read `var isCloudProxy: Bool` returned "No
1087
+ // results found"); pushed below so the getter's calls attribute to it
1088
+ // rather than flattening onto the owning type (SwiftUI `var body: some
1089
+ // View { … }` — the whole subview tree — is the canonical case).
1090
+ const prop = this.createNode('property', (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source), node, {
1091
+ visibility: this.extractor.getVisibility?.(node),
1092
+ isStatic: this.extractor.isStatic?.(node) ?? false,
1093
+ });
1094
+ computedPropId = prop?.id;
1095
+ }
1096
+ else {
1097
+ // A `static let`/`static var` member is a SHARED constant of the type
1098
+ // (esp. in `enum`/`struct`); an instance stored property stays a `field`
1099
+ // (per-instance — Swift instance properties otherwise aren't own nodes).
1100
+ const isStatic = this.extractor.isStatic?.(node) ?? false;
1101
+ this.createNode(isStatic ? (isLet ? 'constant' : 'variable') : 'field', (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source), node, {
1102
+ visibility: this.extractor.getVisibility?.(node),
1103
+ isStatic,
1104
+ });
1105
+ }
1006
1106
  }
1007
1107
  if (ownerId) {
1008
1108
  this.extractDecoratorsFor(node, ownerId);
@@ -1028,6 +1128,20 @@ class TreeSitterExtractor {
1028
1128
  walkAttrArgs(modifiers);
1029
1129
  }
1030
1130
  }
1131
+ // A computed property's getter holds real logic — walk it with the property
1132
+ // node pushed so its calls/instantiations attribute to the property (a
1133
+ // SwiftUI `body`'s subview tree becomes the property's callees). skipChildren
1134
+ // then stops the generic walker from re-walking the getter (and the
1135
+ // modifiers/type annotation already handled above).
1136
+ if (computedPropId) {
1137
+ const getter = node.namedChildren.find((c) => c.type === 'computed_property' || c.type === 'protocol_property_requirements');
1138
+ if (getter) {
1139
+ this.nodeStack.push(computedPropId);
1140
+ this.visitFunctionBody(getter, '');
1141
+ this.nodeStack.pop();
1142
+ }
1143
+ skipChildren = true;
1144
+ }
1031
1145
  }
1032
1146
  // `export_statement` itself is not extracted — the walker descends
1033
1147
  // into children, where the inner declaration (lexical_declaration,
@@ -1055,7 +1169,8 @@ class TreeSitterExtractor {
1055
1169
  // falls through to its normal declaration extraction).
1056
1170
  else if (nodeType === 'export_statement' &&
1057
1171
  (this.language === 'typescript' || this.language === 'tsx' ||
1058
- this.language === 'javascript' || this.language === 'jsx') &&
1172
+ this.language === 'javascript' || this.language === 'jsx' ||
1173
+ this.language === 'arkts') &&
1059
1174
  (0, tree_sitter_helpers_1.getChildByField)(node, 'source')) {
1060
1175
  const parentId = this.nodeStack[this.nodeStack.length - 1];
1061
1176
  if (parentId)
@@ -1085,7 +1200,7 @@ class TreeSitterExtractor {
1085
1200
  // produce an `instantiates` reference. Children still walked so
1086
1201
  // nested calls inside the constructor args (`new Foo(bar())`) get
1087
1202
  // their own `calls` refs.
1088
- else if (INSTANTIATION_KINDS.has(nodeType)) {
1203
+ else if (INSTANTIATION_KINDS.has(nodeType) || this.isVbnetConstructorShapedArrayCreation(node)) {
1089
1204
  this.extractInstantiation(node);
1090
1205
  // Java/C# `new T(...) { ... }` — anonymous class with body. Without
1091
1206
  // extracting it as a class node + its methods, the interface→impl
@@ -1236,7 +1351,8 @@ class TreeSitterExtractor {
1236
1351
  buildQualifiedName(name) {
1237
1352
  // Build a qualified name from the semantic hierarchy only (no file path).
1238
1353
  // The file path is stored separately in filePath and pollutes FTS if included here.
1239
- const parts = [];
1354
+ // C/C++ enclosing namespaces prefix first (empty for every other language).
1355
+ const parts = [...this.namespacePrefix];
1240
1356
  for (const nodeId of this.nodeStack) {
1241
1357
  const node = this.nodes.find((n) => n.id === nodeId);
1242
1358
  if (node && node.kind !== 'file') {
@@ -1455,6 +1571,15 @@ class TreeSitterExtractor {
1455
1571
  extractClass(node, kind = 'class') {
1456
1572
  if (!this.extractor)
1457
1573
  return;
1574
+ // Skip forward declarations / elaborated type references (`class Foo;`) in
1575
+ // languages that opt in — bodiless there means "not a definition", so it
1576
+ // would otherwise mint a phantom node competing with the real definition
1577
+ // (#1093). Languages where a bodiless class is complete (Kotlin, Scala)
1578
+ // leave the flag unset. Resolved once here and reused for the body walk.
1579
+ const resolvedBody = this.extractor.resolveBody?.(node, this.extractor.bodyField)
1580
+ ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
1581
+ if (this.extractor.skipBodilessClass && !resolvedBody)
1582
+ return;
1458
1583
  const name = extractName(node, this.source, this.extractor);
1459
1584
  const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
1460
1585
  const visibility = this.extractor.getVisibility?.(node);
@@ -1474,10 +1599,7 @@ class TreeSitterExtractor {
1474
1599
  this.extractDecoratorsFor(node, classNode.id);
1475
1600
  // Push to stack and visit body
1476
1601
  this.nodeStack.push(classNode.id);
1477
- let body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
1478
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
1479
- if (!body)
1480
- body = node;
1602
+ const body = resolvedBody ?? node;
1481
1603
  // Visit all children for methods and properties
1482
1604
  for (let i = 0; i < body.namedChildCount; i++) {
1483
1605
  const child = body.namedChild(i);
@@ -2311,7 +2433,8 @@ class TreeSitterExtractor {
2311
2433
  const isExported = this.extractor.isExported?.(node, this.source) ?? false;
2312
2434
  // Extract variable declarators based on language
2313
2435
  if (this.language === 'typescript' || this.language === 'javascript' ||
2314
- this.language === 'tsx' || this.language === 'jsx') {
2436
+ this.language === 'tsx' || this.language === 'jsx' || this.language === 'cfscript' ||
2437
+ this.language === 'arkts') {
2315
2438
  // Handle lexical_declaration and variable_declaration
2316
2439
  // These contain one or more variable_declarator children
2317
2440
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -2725,7 +2848,7 @@ class TreeSitterExtractor {
2725
2848
  // property/method nodes under the type alias so `recorder.stop()`
2726
2849
  // can attach the call edge to `RecorderHandle.stop` instead of
2727
2850
  // an unrelated class method picked by path-proximity (#359).
2728
- if (this.language === 'typescript' || this.language === 'tsx') {
2851
+ if (this.language === 'typescript' || this.language === 'tsx' || this.language === 'arkts') {
2729
2852
  this.extractTsTypeAliasMembers(value, typeAliasNode);
2730
2853
  // `type List = [ Service<'name', Req, Resp>, … ]` — surface each
2731
2854
  // entry's string-literal name as a searchable member (issue #634).
@@ -2946,7 +3069,8 @@ class TreeSitterExtractor {
2946
3069
  // Link each imported binding to its definition so imported-but-not-
2947
3070
  // called/typed symbols still record a cross-file dependency (TS/JS only).
2948
3071
  if (this.language === 'typescript' || this.language === 'tsx' ||
2949
- this.language === 'javascript' || this.language === 'jsx') {
3072
+ this.language === 'javascript' || this.language === 'jsx' ||
3073
+ this.language === 'arkts') {
2950
3074
  const parentId = this.nodeStack[this.nodeStack.length - 1];
2951
3075
  if (parentId)
2952
3076
  this.emitImportBindingRefs(node, parentId);
@@ -3385,12 +3509,522 @@ class TreeSitterExtractor {
3385
3509
  /**
3386
3510
  * Extract a function call
3387
3511
  */
3512
+ /**
3513
+ * The module an Erlang gen_server target expression statically names, or
3514
+ * null when it's dynamic (pid/var/tuple form). Static shapes:
3515
+ * - a bare atom — either this module or another one; OTP's dominant
3516
+ * registration convention (`{local, ?MODULE}`) names a server process
3517
+ * after its module, so `gen_server:call(other_mod, …)` reaches
3518
+ * `other_mod`'s handlers. A registered name that matches no module
3519
+ * resolves to nothing downstream (the qualified ref just drops).
3520
+ * - `?MODULE`, or a macro the file defines as `?MODULE`
3521
+ * (`-define(SERVER, ?MODULE)` — the standard self idiom)
3522
+ * - a macro the file defines as a bare atom
3523
+ * (`-define(STORE, kv_store)` — the cross-module variant)
3524
+ * The macro tables are memoized per file (single entry — extraction is
3525
+ * file-sequential).
3526
+ */
3527
+ erlangServerMacroFile = '';
3528
+ erlangSelfMacros = new Set();
3529
+ erlangAtomMacros = new Map();
3530
+ resolveErlangGenServerTarget(target) {
3531
+ const ownModule = (this.filePath.split('/').pop() ?? '').replace(/\.erl$/, '');
3532
+ if (target.type === 'atom') {
3533
+ const name = (0, tree_sitter_helpers_1.getNodeText)(target, this.source).replace(/^'([\s\S]*)'$/, '$1');
3534
+ return name || null;
3535
+ }
3536
+ if (target.type !== 'macro_call_expr')
3537
+ return null;
3538
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(target, 'name');
3539
+ if (!nameNode)
3540
+ return null;
3541
+ const macroName = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
3542
+ if (macroName === 'MODULE')
3543
+ return ownModule || null;
3544
+ if (this.erlangServerMacroFile !== this.filePath) {
3545
+ this.erlangServerMacroFile = this.filePath;
3546
+ this.erlangSelfMacros = new Set();
3547
+ this.erlangAtomMacros = new Map();
3548
+ let root = target;
3549
+ while (root.parent)
3550
+ root = root.parent;
3551
+ for (let i = 0; i < root.namedChildCount; i++) {
3552
+ const form = root.namedChild(i);
3553
+ if (form?.type !== 'pp_define')
3554
+ continue;
3555
+ const lhs = (0, tree_sitter_helpers_1.getChildByField)(form, 'lhs');
3556
+ const defName = lhs ? (0, tree_sitter_helpers_1.getChildByField)(lhs, 'name') : null;
3557
+ const replacement = (0, tree_sitter_helpers_1.getChildByField)(form, 'replacement');
3558
+ if (!defName || !replacement)
3559
+ continue;
3560
+ if (replacement.type === 'macro_call_expr' &&
3561
+ (0, tree_sitter_helpers_1.getChildByField)(replacement, 'name') &&
3562
+ (0, tree_sitter_helpers_1.getNodeText)((0, tree_sitter_helpers_1.getChildByField)(replacement, 'name'), this.source) === 'MODULE') {
3563
+ this.erlangSelfMacros.add((0, tree_sitter_helpers_1.getNodeText)(defName, this.source));
3564
+ }
3565
+ else if (replacement.type === 'atom') {
3566
+ this.erlangAtomMacros.set((0, tree_sitter_helpers_1.getNodeText)(defName, this.source), (0, tree_sitter_helpers_1.getNodeText)(replacement, this.source).replace(/^'([\s\S]*)'$/, '$1'));
3567
+ }
3568
+ }
3569
+ }
3570
+ if (this.erlangSelfMacros.has(macroName))
3571
+ return ownModule || null;
3572
+ return this.erlangAtomMacros.get(macroName) ?? null;
3573
+ }
3388
3574
  extractCall(node) {
3389
3575
  if (this.nodeStack.length === 0)
3390
3576
  return;
3391
3577
  const callerId = this.nodeStack[this.nodeStack.length - 1];
3392
3578
  if (!callerId)
3393
3579
  return;
3580
+ // VB.NET: `foo(args)` is syntactically ambiguous between a call and an
3581
+ // index read, so the grammar parses non-empty parens as
3582
+ // array_access_expression (field `array`, not `function`) — even Roslyn
3583
+ // parses both as InvocationExpression and resolves during binding. Treat
3584
+ // all three shapes as call sites: the callee is the member/identifier
3585
+ // under the array/function field, qualified with a simple-identifier
3586
+ // receiver for resolution. Index reads on collections simply never
3587
+ // resolve to a callable, so they cost nothing.
3588
+ if (this.language === 'vbnet' &&
3589
+ (node.type === 'array_access_expression' ||
3590
+ node.type === 'invocation_expression' ||
3591
+ node.type === 'generic_invocation_expression')) {
3592
+ const fn = (0, tree_sitter_helpers_1.getChildByField)(node, 'function') || (0, tree_sitter_helpers_1.getChildByField)(node, 'array');
3593
+ if (!fn)
3594
+ return;
3595
+ let calleeName = '';
3596
+ if (fn.type === 'member_access_expression') {
3597
+ const member = (0, tree_sitter_helpers_1.getChildByField)(fn, 'member');
3598
+ const memberName = member ? (0, tree_sitter_helpers_1.getNodeText)(member, this.source) : '';
3599
+ if (!memberName)
3600
+ return;
3601
+ const receiver = (0, tree_sitter_helpers_1.getChildByField)(fn, 'object');
3602
+ const SKIP = new Set(['me', 'mybase', 'myclass']);
3603
+ if (receiver && receiver.type === 'identifier' && !SKIP.has((0, tree_sitter_helpers_1.getNodeText)(receiver, this.source).toLowerCase())) {
3604
+ calleeName = `${(0, tree_sitter_helpers_1.getNodeText)(receiver, this.source)}.${memberName}`;
3605
+ }
3606
+ else {
3607
+ calleeName = memberName;
3608
+ }
3609
+ }
3610
+ else if (fn.type === 'identifier') {
3611
+ calleeName = (0, tree_sitter_helpers_1.getNodeText)(fn, this.source);
3612
+ }
3613
+ else {
3614
+ return; // parenthesized/chained receivers: no static name to link
3615
+ }
3616
+ if (calleeName) {
3617
+ this.unresolvedReferences.push({
3618
+ fromNodeId: callerId,
3619
+ referenceName: calleeName,
3620
+ referenceKind: 'calls',
3621
+ line: node.startPosition.row + 1,
3622
+ column: node.startPosition.column,
3623
+ });
3624
+ }
3625
+ return;
3626
+ }
3627
+ // Erlang: a local call is `call(expr: atom, args)`; a remote call nests it
3628
+ // under `remote(module: remote_module, fun: call)` — the module qualifier
3629
+ // lives on the PARENT. Remote calls are emitted as `mod::fn`, which is
3630
+ // byte-identical to the qualifiedName the module namespace gives every
3631
+ // function (see packageTypes in languages/erlang.ts), so they resolve via
3632
+ // matchByQualifiedName. A var/macro callee or module (`F(X)`, `?M(X)`,
3633
+ // `Mod:handle(X)`) has no static target — except `?MODULE:fn(X)`, which the
3634
+ // bare name + same-file preference resolves correctly. `fun name/1` /
3635
+ // `fun mod:name/1` values are function REFERENCES (callback registration),
3636
+ // and record construction/update/index/field-access are `references` to the
3637
+ // record's struct node.
3638
+ if (this.language === 'erlang') {
3639
+ const line = node.startPosition.row + 1;
3640
+ const column = node.startPosition.column;
3641
+ const erlAtom = (n) => (0, tree_sitter_helpers_1.getNodeText)(n, this.source).replace(/^'([\s\S]*)'$/, '$1');
3642
+ if (node.type === 'call') {
3643
+ let callee = (0, tree_sitter_helpers_1.getChildByField)(node, 'expr');
3644
+ let moduleNode = null;
3645
+ // remote(module, fun: call) — the shape the grammar produces today; the
3646
+ // node-types also permit call(expr: remote), so handle both nestings.
3647
+ if (node.parent?.type === 'remote') {
3648
+ moduleNode = (0, tree_sitter_helpers_1.getChildByField)(node.parent, 'module');
3649
+ }
3650
+ else if (callee?.type === 'remote') {
3651
+ moduleNode = (0, tree_sitter_helpers_1.getChildByField)(callee, 'module');
3652
+ callee = (0, tree_sitter_helpers_1.getChildByField)(callee, 'fun');
3653
+ }
3654
+ if (callee?.type === 'atom') {
3655
+ const fnBare = erlAtom(callee);
3656
+ let calleeName = fnBare;
3657
+ const moduleExpr = moduleNode ? (0, tree_sitter_helpers_1.getChildByField)(moduleNode, 'module') : null;
3658
+ if (moduleExpr?.type === 'atom') {
3659
+ calleeName = `${erlAtom(moduleExpr)}::${calleeName}`;
3660
+ }
3661
+ else if (moduleExpr) {
3662
+ // Non-atom module qualifier. `?MODULE:f(X)` targets THIS module —
3663
+ // keep the bare name so same-file preference resolves it. Anything
3664
+ // else (`Mod:f(X)`) is behaviour-style dynamic dispatch with no
3665
+ // static target: emitting the bare name would link an arbitrary
3666
+ // same-named function, so stay silent instead.
3667
+ const macroName = moduleExpr.type === 'macro_call_expr' ? (0, tree_sitter_helpers_1.getChildByField)(moduleExpr, 'name') : null;
3668
+ if (!macroName || (0, tree_sitter_helpers_1.getNodeText)(macroName, this.source) !== 'MODULE')
3669
+ return;
3670
+ }
3671
+ this.unresolvedReferences.push({
3672
+ fromNodeId: callerId,
3673
+ referenceName: calleeName,
3674
+ referenceKind: 'calls',
3675
+ line,
3676
+ column,
3677
+ });
3678
+ // gen_server dispatch: `gen_server:call(?SERVER, Msg)` /
3679
+ // `gen_server:cast(other_mod, Msg)` — a request routes to the TARGET
3680
+ // module's handle_call/handle_cast. The target is static when the
3681
+ // first argument names a module: ?MODULE or a ?MODULE-defined macro
3682
+ // (the self API-wrapper idiom), a bare atom (OTP's `{local, ?MODULE}`
3683
+ // convention names a server after its module, so a cross-module
3684
+ // registered name reaches that module's handlers — and a registered
3685
+ // name matching no module resolves to nothing), or a macro defined
3686
+ // as a bare atom. Pid/var/tuple targets stay silent.
3687
+ if (moduleExpr?.type === 'atom' &&
3688
+ erlAtom(moduleExpr) === 'gen_server' &&
3689
+ (fnBare === 'call' || fnBare === 'cast' || fnBare === 'send_request')) {
3690
+ const argsNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'args');
3691
+ const target = argsNode?.namedChild(0) ?? null;
3692
+ const targetModule = target ? this.resolveErlangGenServerTarget(target) : null;
3693
+ if (targetModule) {
3694
+ this.unresolvedReferences.push({
3695
+ fromNodeId: callerId,
3696
+ referenceName: `${targetModule}::${fnBare === 'cast' ? 'handle_cast' : 'handle_call'}`,
3697
+ referenceKind: 'calls',
3698
+ line,
3699
+ column,
3700
+ });
3701
+ }
3702
+ }
3703
+ // MFA-in-argument dispatch: the spawn/apply family names its real
3704
+ // callee in ARGUMENT position — `proc_lib:spawn_link(?MODULE,
3705
+ // request_process, [Req, Env, Middlewares])` — so the walker above
3706
+ // sees only the spawn itself and the spawned function ends up with
3707
+ // zero callers (measured on cowboy: request_process had no incoming
3708
+ // edges and the agent Read the file to find it). When the (Module,
3709
+ // Function) pair is static, lift it as a call edge. The pair is
3710
+ // found positionally-agnostically (first adjacent module-atom/
3711
+ // ?MODULE + atom pair) so every arity variant works: spawn/3,
3712
+ // spawn(Node,M,F,A)/4, timer:apply_after(Time,M,F,A),
3713
+ // rpc:call(Node,M,F,A). A var module or fun stays silent.
3714
+ const familyKey = moduleExpr?.type === 'atom' ? `${erlAtom(moduleExpr)}:${fnBare}` : fnBare;
3715
+ if (ERLANG_MFA_CALLS.has(familyKey)) {
3716
+ const argsNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'args');
3717
+ const argExprs = argsNode ? argsNode.namedChildren : [];
3718
+ for (let i = 0; i + 1 < argExprs.length; i++) {
3719
+ const m = argExprs[i];
3720
+ const f = argExprs[i + 1];
3721
+ if (f.type !== 'atom')
3722
+ continue;
3723
+ const isLocalModule = m.type === 'macro_call_expr' &&
3724
+ (0, tree_sitter_helpers_1.getChildByField)(m, 'name') !== null &&
3725
+ (0, tree_sitter_helpers_1.getNodeText)((0, tree_sitter_helpers_1.getChildByField)(m, 'name'), this.source) === 'MODULE';
3726
+ if (m.type !== 'atom' && !isLocalModule)
3727
+ continue;
3728
+ this.unresolvedReferences.push({
3729
+ fromNodeId: callerId,
3730
+ referenceName: isLocalModule ? erlAtom(f) : `${erlAtom(m)}::${erlAtom(f)}`,
3731
+ referenceKind: 'calls',
3732
+ line: f.startPosition.row + 1,
3733
+ column: f.startPosition.column,
3734
+ });
3735
+ break;
3736
+ }
3737
+ }
3738
+ }
3739
+ return;
3740
+ }
3741
+ if (node.type === 'internal_fun' || node.type === 'external_fun') {
3742
+ const funNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'fun');
3743
+ if (funNode?.type !== 'atom')
3744
+ return; // fun Mod:F/A with var parts — dynamic
3745
+ let refName = erlAtom(funNode);
3746
+ if (node.type === 'external_fun') {
3747
+ const moduleWrapper = (0, tree_sitter_helpers_1.getChildByField)(node, 'module');
3748
+ const moduleAtom = moduleWrapper ? (0, tree_sitter_helpers_1.getChildByField)(moduleWrapper, 'name') : null;
3749
+ if (moduleAtom?.type !== 'atom')
3750
+ return;
3751
+ refName = `${erlAtom(moduleAtom)}::${refName}`;
3752
+ }
3753
+ this.unresolvedReferences.push({
3754
+ fromNodeId: callerId,
3755
+ referenceName: refName,
3756
+ referenceKind: 'references',
3757
+ line,
3758
+ column,
3759
+ });
3760
+ return;
3761
+ }
3762
+ if (node.type === 'macro_call_expr') {
3763
+ // Macro use site → the `-define` constant node. Function-like uses
3764
+ // (`?LOG_AUDIT(X)` — args present) are inlined code, so they join the
3765
+ // call chain and connect through the macro node to the body's calls
3766
+ // (attributed there by handlePpDefine); bare reads (`?TIMEOUT`) are
3767
+ // `references`, answering "where is this macro used" without
3768
+ // polluting call paths. Compiler-predefined macros carry no
3769
+ // definition to link. The use site's ARGUMENTS are children and keep
3770
+ // walking, so a call nested in `?assertEqual(ok, do_thing())` still
3771
+ // attributes to the enclosing function.
3772
+ const macroName = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
3773
+ if (!macroName)
3774
+ return;
3775
+ const name = (0, tree_sitter_helpers_1.getNodeText)(macroName, this.source);
3776
+ if (ERLANG_PREDEFINED_MACROS.has(name))
3777
+ return;
3778
+ this.unresolvedReferences.push({
3779
+ fromNodeId: callerId,
3780
+ referenceName: name,
3781
+ referenceKind: (0, tree_sitter_helpers_1.getChildByField)(node, 'args') ? 'calls' : 'references',
3782
+ line,
3783
+ column,
3784
+ });
3785
+ return;
3786
+ }
3787
+ // record_expr / record_update_expr / record_index_expr / record_field_expr
3788
+ const recordName = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
3789
+ const recordAtom = recordName?.type === 'record_name' ? (0, tree_sitter_helpers_1.getChildByField)(recordName, 'name') : null;
3790
+ if (recordAtom?.type === 'atom') {
3791
+ this.unresolvedReferences.push({
3792
+ fromNodeId: callerId,
3793
+ referenceName: erlAtom(recordAtom),
3794
+ referenceKind: 'references',
3795
+ line,
3796
+ column,
3797
+ });
3798
+ }
3799
+ return;
3800
+ }
3801
+ // Ruby `call` nodes use `receiver` + `method` fields (tree-sitter-ruby), not
3802
+ // the `object`/`name`/`function` fields the branches below expect — so
3803
+ // without this they fell through to the generic path, which took the
3804
+ // receiver as the callee and DROPPED the method name: `lg.log()` produced a
3805
+ // `calls` ref to `lg` (unresolvable) and no method edge was ever recorded,
3806
+ // so a Ruby method's callers/impact were invisible (#1108 follow-up). Build
3807
+ // `receiver.method` so the resolver — and local-variable type inference —
3808
+ // can link it; `Foo.new` stays an instantiation.
3809
+ if (this.language === 'ruby' && (node.type === 'call' || node.type === 'method_call')) {
3810
+ const methodNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'method');
3811
+ const methodName = methodNode ? (0, tree_sitter_helpers_1.getNodeText)(methodNode, this.source) : '';
3812
+ if (!methodName)
3813
+ return; // operator/element-reference call with no method name
3814
+ const receiverNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'receiver');
3815
+ const line = node.startPosition.row + 1;
3816
+ const column = node.startPosition.column;
3817
+ if (!receiverNode) {
3818
+ // Bare `foo(...)` — just the method name (unchanged behavior).
3819
+ this.unresolvedReferences.push({ fromNodeId: callerId, referenceName: methodName, referenceKind: 'calls', line, column });
3820
+ return;
3821
+ }
3822
+ const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiverNode, this.source);
3823
+ // `Foo.new` / `Foo::Bar.new` is construction — emit an `instantiates` ref to
3824
+ // the class (last `::` segment), preserving the "what creates X" edge.
3825
+ if (methodName === 'new') {
3826
+ const className = receiverName.includes('::')
3827
+ ? receiverName.slice(receiverName.lastIndexOf('::') + 2)
3828
+ : receiverName;
3829
+ if (/^[A-Z]/.test(className)) {
3830
+ this.unresolvedReferences.push({ fromNodeId: callerId, referenceName: className, referenceKind: 'instantiates', line, column });
3831
+ return;
3832
+ }
3833
+ }
3834
+ const SKIP_RECEIVERS = new Set(['self', 'super']);
3835
+ const skip = SKIP_RECEIVERS.has(receiverName);
3836
+ this.unresolvedReferences.push({
3837
+ fromNodeId: callerId,
3838
+ referenceName: skip ? methodName : `${receiverName}.${methodName}`,
3839
+ referenceKind: 'calls',
3840
+ line,
3841
+ column,
3842
+ });
3843
+ // A capitalized (constant) receiver — `Foo.bar`, a class/module method call
3844
+ // — is itself a dependency on that constant; emit a `references` ref so a
3845
+ // class used only via its class methods still records a dependent (the edge
3846
+ // the old receiver-only callee happened to provide, now made explicit).
3847
+ if (!skip && receiverNode.type === 'constant') {
3848
+ this.unresolvedReferences.push({
3849
+ fromNodeId: callerId,
3850
+ referenceName: receiverName,
3851
+ referenceKind: 'references',
3852
+ line: receiverNode.startPosition.row + 1,
3853
+ column: receiverNode.startPosition.column,
3854
+ });
3855
+ }
3856
+ return;
3857
+ }
3858
+ // ArkTS build()-DSL handling. Three shapes carry UI-attribute chains, and
3859
+ // all of their attribute names are emitted with a LEADING DOT
3860
+ // (`.titleStyle`, `.width`) — an impossible identifier that routes them to
3861
+ // a dedicated matcher strategy resolving ONLY to decorator-marked
3862
+ // attribute helpers (`@Extend`/`@Styles`/`@AnimatableExtend`/`@Builder`
3863
+ // functions). Bare names would go through global name matching, where
3864
+ // framework attributes (`.width`, `.fontSize`, appearing on nearly every
3865
+ // UI line) hit arbitrary same-named symbols — measured on the OpenHarmony
3866
+ // samples monorepo, that produced 36k wrong edges (17% of all calls),
3867
+ // including single properties with 3,400+ false callers.
3868
+ //
3869
+ // 1. `Column({space:8}) { … }.height('100%')` — ONE
3870
+ // arkui_component_expression: `function:` = the component, chained
3871
+ // attributes as repeated `property:`/`arguments:` field pairs.
3872
+ // The component ref (`Column`, `TodoRow`) stays a PLAIN name — it
3873
+ // resolves to the child `@Component struct`, giving the parent→child
3874
+ // component-tree edge the way JSX children do for React.
3875
+ // 2. `Image(x).width(10).onClick(this.f)` — ordinary nested
3876
+ // call_expressions whose `function:` is a member_expression chained
3877
+ // on a CALL RESULT (never a named receiver, so `svc.save()` /
3878
+ // `this.vm.load()` are untouched and fall through to the generic
3879
+ // paths below).
3880
+ // 3. A nested component whose chain starts on the line AFTER its
3881
+ // closing `}` inside arkui_children — the grammar detaches the chain
3882
+ // into sibling `leading_dot_expression(identifier)` +
3883
+ // `parenthesized_expression(args)` statement pairs; reassemble from
3884
+ // the siblings.
3885
+ //
3886
+ // `.onXxx(this.handler)` METHOD-REFERENCE bindings (no call parens, so
3887
+ // nothing else records them) additionally emit a call ref to the bare
3888
+ // handler name — same-class resolution links the tap→handler hop.
3889
+ // Arrow-function handlers need nothing: their bodies' calls already
3890
+ // attribute to the enclosing build(). Children/argument subtrees are
3891
+ // still walked by the caller, so nested components extract normally.
3892
+ if (this.language === 'arkts') {
3893
+ const emitAttr = (nameNode) => {
3894
+ const attrName = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
3895
+ if (!attrName)
3896
+ return;
3897
+ this.unresolvedReferences.push({
3898
+ fromNodeId: callerId,
3899
+ referenceName: '.' + attrName,
3900
+ referenceKind: 'calls',
3901
+ line: nameNode.startPosition.row + 1,
3902
+ column: nameNode.startPosition.column,
3903
+ });
3904
+ };
3905
+ // Emit `handler` for each bare `this.handler` among an on-attribute's
3906
+ // arguments.
3907
+ const emitThisHandlers = (args) => {
3908
+ if (!args)
3909
+ return;
3910
+ for (let j = 0; j < args.namedChildCount; j++) {
3911
+ const arg = args.namedChild(j);
3912
+ if (arg?.type !== 'member_expression')
3913
+ continue;
3914
+ const obj = (0, tree_sitter_helpers_1.getChildByField)(arg, 'object');
3915
+ const prop = (0, tree_sitter_helpers_1.getChildByField)(arg, 'property');
3916
+ if (obj?.type === 'this' && prop) {
3917
+ this.unresolvedReferences.push({
3918
+ fromNodeId: callerId,
3919
+ referenceName: (0, tree_sitter_helpers_1.getNodeText)(prop, this.source),
3920
+ referenceKind: 'calls',
3921
+ line: arg.startPosition.row + 1,
3922
+ column: arg.startPosition.column,
3923
+ });
3924
+ }
3925
+ }
3926
+ };
3927
+ // Shape 1: arkui_component_expression with property/arguments pairs.
3928
+ if (node.type === 'arkui_component_expression') {
3929
+ const componentField = (0, tree_sitter_helpers_1.getChildByField)(node, 'function');
3930
+ if (componentField && componentField.type === 'identifier') {
3931
+ this.unresolvedReferences.push({
3932
+ fromNodeId: callerId,
3933
+ referenceName: (0, tree_sitter_helpers_1.getNodeText)(componentField, this.source),
3934
+ referenceKind: 'calls',
3935
+ line: node.startPosition.row + 1,
3936
+ column: node.startPosition.column,
3937
+ });
3938
+ }
3939
+ for (let i = 0; i < node.childCount; i++) {
3940
+ const child = node.child(i);
3941
+ if (!child || child.type !== 'property_identifier')
3942
+ continue;
3943
+ emitAttr(child);
3944
+ if (/^on[A-Z]/.test((0, tree_sitter_helpers_1.getNodeText)(child, this.source))) {
3945
+ // The attribute's arguments node is the next `arguments`-typed
3946
+ // child before the following attribute name.
3947
+ let args = null;
3948
+ for (let k = i + 1; k < node.childCount; k++) {
3949
+ const next = node.child(k);
3950
+ if (!next)
3951
+ continue;
3952
+ if (next.type === 'property_identifier')
3953
+ break;
3954
+ if (next.type === 'arguments') {
3955
+ args = next;
3956
+ break;
3957
+ }
3958
+ }
3959
+ emitThisHandlers(args);
3960
+ }
3961
+ }
3962
+ return;
3963
+ }
3964
+ // Shape 2: fluent chain on a call result —
3965
+ // call_expression(function: member_expression(object: <call>)), or the
3966
+ // grammar's DSL-specific arkui_dsl_decorator_member_expression (same
3967
+ // object/property fields; produced e.g. by `Column() { … }.alignItems(x)`
3968
+ // in some chain positions — it ONLY occurs in attribute chains).
3969
+ if (node.type === 'call_expression') {
3970
+ const fn = (0, tree_sitter_helpers_1.getChildByField)(node, 'function');
3971
+ if (fn?.type === 'member_expression' || fn?.type === 'arkui_dsl_decorator_member_expression') {
3972
+ const obj = (0, tree_sitter_helpers_1.getChildByField)(fn, 'object');
3973
+ const prop = (0, tree_sitter_helpers_1.getChildByField)(fn, 'property');
3974
+ if (prop &&
3975
+ (fn.type === 'arkui_dsl_decorator_member_expression' ||
3976
+ obj?.type === 'call_expression' ||
3977
+ obj?.type === 'arkui_component_expression')) {
3978
+ emitAttr(prop);
3979
+ if (/^on[A-Z]/.test((0, tree_sitter_helpers_1.getNodeText)(prop, this.source))) {
3980
+ emitThisHandlers((0, tree_sitter_helpers_1.getChildByField)(node, 'arguments'));
3981
+ }
3982
+ return;
3983
+ }
3984
+ }
3985
+ // The INNERMOST call of a proper-form detached chain
3986
+ // (`.alignItems(x).layoutWeight(1)…` under a leading_dot_expression)
3987
+ // has a BARE IDENTIFIER function — the leading dot was consumed by
3988
+ // the wrapper, so it masquerades as a plain `alignItems(...)` call.
3989
+ // Walk up the member/call alternation; topping out at
3990
+ // leading_dot_expression means the dot belongs to this chain.
3991
+ if (fn?.type === 'identifier') {
3992
+ let p = node.parent;
3993
+ while (p && (p.type === 'member_expression' || p.type === 'call_expression')) {
3994
+ p = p.parent;
3995
+ }
3996
+ if (p?.type === 'leading_dot_expression') {
3997
+ emitAttr(fn);
3998
+ if (/^on[A-Z]/.test((0, tree_sitter_helpers_1.getNodeText)(fn, this.source))) {
3999
+ emitThisHandlers((0, tree_sitter_helpers_1.getChildByField)(node, 'arguments'));
4000
+ }
4001
+ return;
4002
+ }
4003
+ }
4004
+ // Not a chained attribute — fall through to the generic call paths.
4005
+ }
4006
+ // Shape 3: detached chain segment — leading_dot_expression whose only
4007
+ // named child is a bare identifier; its arguments sit in the NEXT
4008
+ // sibling statement as a parenthesized_expression.
4009
+ if (node.type === 'leading_dot_expression') {
4010
+ const only = node.namedChildCount === 1 ? node.namedChild(0) : null;
4011
+ if (only && only.type === 'identifier') {
4012
+ emitAttr(only);
4013
+ if (/^on[A-Z]/.test((0, tree_sitter_helpers_1.getNodeText)(only, this.source))) {
4014
+ const stmt = node.parent; // expression_statement
4015
+ const nextStmt = stmt?.nextNamedSibling;
4016
+ const paren = nextStmt?.namedChild(0);
4017
+ if (paren?.type === 'parenthesized_expression') {
4018
+ emitThisHandlers(paren);
4019
+ }
4020
+ }
4021
+ }
4022
+ // The proper form (child is a call_expression chain, as inside
4023
+ // `@Extend` bodies) needs nothing here — the walker descends into it
4024
+ // and the inner call_expressions take the paths above.
4025
+ return;
4026
+ }
4027
+ }
3394
4028
  // Get the function/method being called
3395
4029
  let calleeName = '';
3396
4030
  // Java/Kotlin method_invocation has 'object' + 'name' fields instead of 'function'
@@ -3682,6 +4316,20 @@ class TreeSitterExtractor {
3682
4316
  }
3683
4317
  calleeName = reencode ? `${innerCallee}().${methodName}` : methodName;
3684
4318
  }
4319
+ else if (this.language === 'cfscript' &&
4320
+ receiver &&
4321
+ receiver.type === 'member_expression' &&
4322
+ /^(variables|this|local|arguments)\.[A-Za-z_][\w]*$/i.test((0, tree_sitter_helpers_1.getNodeText)(receiver, this.source))) {
4323
+ // CFML scope-prefixed member call — `variables.svc.save()` /
4324
+ // `arguments.svc.save()`: the receiver is a component field,
4325
+ // injected property, or typed argument reached through one of
4326
+ // CFML's file-local scopes. Keep the full receiver chain so
4327
+ // resolution can strip the scope prefix and infer the field's
4328
+ // component type from its declaration (#1108). Gated to these
4329
+ // scope keywords: such calls previously emitted a bare method
4330
+ // name, which either failed to resolve or resolved ambiguously.
4331
+ calleeName = `${(0, tree_sitter_helpers_1.getNodeText)(receiver, this.source)}.${methodName}`;
4332
+ }
3685
4333
  else {
3686
4334
  calleeName = methodName;
3687
4335
  }
@@ -3724,6 +4372,40 @@ class TreeSitterExtractor {
3724
4372
  if (conv && conv[1])
3725
4373
  calleeName = conv[1];
3726
4374
  }
4375
+ // C/C++ templated callees — a direct templated call (`fn<T, 256>(args)`,
4376
+ // the shape every CUDA kernel-launch site takes once its `<<<…>>>` config
4377
+ // is blanked) or a qualified one (`ns::fn<T>(args)`) — carry template
4378
+ // arguments in the callee text, which can never match the bare name the
4379
+ // function was DEFINED as, so the call edge silently never resolves. Strip
4380
+ // them: the same normalization base-class `extends` refs already get
4381
+ // (#1043). `operator<`/`operator<<` callees are excluded — their `<` is the
4382
+ // operator itself, not a template-argument list.
4383
+ if (calleeName &&
4384
+ calleeName.includes('<') &&
4385
+ (this.language === 'cpp' || this.language === 'c') &&
4386
+ !calleeName.includes('operator')) {
4387
+ calleeName = (0, c_cpp_1.stripCppTemplateArgs)(calleeName);
4388
+ }
4389
+ // C++ call/launch through a local function pointer: `auto kernel =
4390
+ // &flash_fwd_kernel<…>; … kernel<<<grid, block>>>(params);` — the callee
4391
+ // is an unresolvable local name. When the same enclosing symbol bound the
4392
+ // local from `&fn` (each branch assignment counts), emit the call against
4393
+ // every recorded target instead of the local.
4394
+ if (calleeName && this.language === 'cpp' && /^[A-Za-z_]\w*$/.test(calleeName)) {
4395
+ const targets = this.cppLocalFnPtrs.get(callerId)?.get(calleeName);
4396
+ if (targets && targets.size > 0) {
4397
+ for (const target of targets) {
4398
+ this.unresolvedReferences.push({
4399
+ fromNodeId: callerId,
4400
+ referenceName: target,
4401
+ referenceKind: 'calls',
4402
+ line: node.startPosition.row + 1,
4403
+ column: node.startPosition.column,
4404
+ });
4405
+ }
4406
+ return;
4407
+ }
4408
+ }
3727
4409
  if (calleeName) {
3728
4410
  this.unresolvedReferences.push({
3729
4411
  fromNodeId: callerId,
@@ -3743,6 +4425,26 @@ class TreeSitterExtractor {
3743
4425
  * Children are still walked so nested calls inside the constructor
3744
4426
  * arguments (`new Foo(bar())`) get their own `calls` references.
3745
4427
  */
4428
+ /**
4429
+ * VB.NET `New Invoice(1)` is syntactically ambiguous between constructing
4430
+ * Invoice with an argument and allocating an Invoice array of bound 1; the
4431
+ * grammar parses the parenthesized form as array_creation_expression. A
4432
+ * user-defined type with no `{...}` array initializer is overwhelmingly a
4433
+ * constructor call, so treat it as an instantiation. Predefined element
4434
+ * types (`New Byte(1023)`) and brace-initialized forms stay arrays.
4435
+ */
4436
+ isVbnetConstructorShapedArrayCreation(node) {
4437
+ if (this.language !== 'vbnet' || node.type !== 'array_creation_expression')
4438
+ return false;
4439
+ const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
4440
+ if (!typeNode || typeNode.type === 'predefined_type' || typeNode.type === 'array_type')
4441
+ return false;
4442
+ for (const child of node.namedChildren) {
4443
+ if (child?.type === 'array_initializer')
4444
+ return false;
4445
+ }
4446
+ return true;
4447
+ }
3746
4448
  extractInstantiation(node) {
3747
4449
  if (this.nodeStack.length === 0)
3748
4450
  return;
@@ -3805,6 +4507,14 @@ class TreeSitterExtractor {
3805
4507
  const ltIdx = className.indexOf('<');
3806
4508
  if (ltIdx > 0)
3807
4509
  className = className.slice(0, ltIdx);
4510
+ // VB.NET spells generics with parentheses: `New List(Of String)` /
4511
+ // `New Dictionary(Of K, V)(cap)` — strip from the `(` so the bare
4512
+ // type name is what resolution matches.
4513
+ if (this.language === 'vbnet') {
4514
+ const parenIdx = className.indexOf('(');
4515
+ if (parenIdx > 0)
4516
+ className = className.slice(0, parenIdx);
4517
+ }
3808
4518
  // For namespaced/qualified constructors (`new ns.Foo()`,
3809
4519
  // `new ns::Foo()`) keep the trailing identifier — that's what
3810
4520
  // matches a class node in the index.
@@ -3822,6 +4532,45 @@ class TreeSitterExtractor {
3822
4532
  });
3823
4533
  }
3824
4534
  }
4535
+ /**
4536
+ * Is this C++ `declaration` a stack/direct-initialization object construction
4537
+ * that invokes a constructor — `Calculator calc(0)` (direct-init) or
4538
+ * `Widget w{1, 2}` (brace-init) — as opposed to a plain variable or a
4539
+ * function declaration? Used to emit an `instantiates` edge for the
4540
+ * call-less construction syntax (#1035); heap `new T(...)` is handled
4541
+ * separately by INSTANTIATION_KINDS.
4542
+ *
4543
+ * Two signals, both required:
4544
+ * - the `type` field is a class-like NAMED type (`type_identifier`,
4545
+ * `template_type`, or `qualified_identifier`). Primitives (`int x(0)`),
4546
+ * `auto` (`placeholder_type_specifier` — that form always carries a real
4547
+ * `call_expression`, already handled), and sized specifiers are excluded —
4548
+ * they construct no class; and
4549
+ * - a declarator carries constructor arguments: an `init_declarator` whose
4550
+ * `value` is an `argument_list` (`(args)`) or `initializer_list` (`{args}`).
4551
+ * This skips default construction `Calculator c;` (no value) and the
4552
+ * most-vexing-parse `Calculator c();` (a bodyless `function_declarator`,
4553
+ * a function decl — not a construction).
4554
+ */
4555
+ isCppStackConstruction(node) {
4556
+ const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
4557
+ if (!typeNode ||
4558
+ (typeNode.type !== 'type_identifier' &&
4559
+ typeNode.type !== 'template_type' &&
4560
+ typeNode.type !== 'qualified_identifier')) {
4561
+ return false;
4562
+ }
4563
+ for (let i = 0; i < node.namedChildCount; i++) {
4564
+ const child = node.namedChild(i);
4565
+ if (child?.type !== 'init_declarator')
4566
+ continue;
4567
+ const value = (0, tree_sitter_helpers_1.getChildByField)(child, 'value');
4568
+ if (value && (value.type === 'argument_list' || value.type === 'initializer_list')) {
4569
+ return true;
4570
+ }
4571
+ }
4572
+ return false;
4573
+ }
3825
4574
  /**
3826
4575
  * Static-member / value-read pass. A type/enum/class used only via a member
3827
4576
  * VALUE — `Enum.value`, `Type.CONST`, `Colors.red`, `Foo::BAR` — recorded no
@@ -3980,6 +4729,28 @@ class TreeSitterExtractor {
3980
4729
  const consider = (n) => {
3981
4730
  if (!n)
3982
4731
  return;
4732
+ // Solidity `modifier_invocation` (unique to that grammar) sits
4733
+ // decorator-position in the function header — OUTSIDE the `body:` field
4734
+ // the call walker descends — but its body executes around the function
4735
+ // via `_;`, so it is a real call-flow hop (`withdraw → onlyOwner →
4736
+ // _checkRole` is the canonical audit trace). The same node type carries
4737
+ // base-constructor invocations (`constructor() ERC20("T","TOK")`), the
4738
+ // constructor-chain hop. Emit `calls`, not `decorates`, so flow
4739
+ // traversal rides it.
4740
+ if (n.type === 'modifier_invocation') {
4741
+ const target = n.namedChild(0);
4742
+ const name = target?.type === 'identifier' ? (0, tree_sitter_helpers_1.getNodeText)(target, this.source) : undefined;
4743
+ if (name) {
4744
+ this.unresolvedReferences.push({
4745
+ fromNodeId: decoratedId,
4746
+ referenceName: name,
4747
+ referenceKind: 'calls',
4748
+ line: n.startPosition.row + 1,
4749
+ column: n.startPosition.column,
4750
+ });
4751
+ }
4752
+ return;
4753
+ }
3983
4754
  // `marker_annotation` is Java's grammar for arg-less annotations
3984
4755
  // (`@Override`, `@Deprecated`); `attribute` is Swift's grammar for
3985
4756
  // attributes and PROPERTY WRAPPERS (`@objc`, `@Argument`, `@Published`,
@@ -4157,6 +4928,43 @@ class TreeSitterExtractor {
4157
4928
  }
4158
4929
  flush();
4159
4930
  }
4931
+ /**
4932
+ * Record a C++ local function-pointer binding (`local = &fn` / `&fn<…>` /
4933
+ * `&ns::fn<…>`) for the CURRENT enclosing symbol, so calls through the local
4934
+ * resolve to the real target (see cppLocalFnPtrs). Only the address-of shape
4935
+ * is accepted — a bare-identifier RHS (`auto x = y;`) is any value copy, and
4936
+ * linking through it would guess.
4937
+ */
4938
+ recordCppFnPtrBinding(localName, value) {
4939
+ if (!value || value.type !== 'pointer_expression')
4940
+ return;
4941
+ if (value.child(0)?.type !== '&')
4942
+ return; // `*p` dereference, not address-of
4943
+ const arg = (0, tree_sitter_helpers_1.getChildByField)(value, 'argument') ?? value.namedChild(0);
4944
+ if (!arg ||
4945
+ (arg.type !== 'identifier' &&
4946
+ arg.type !== 'template_function' &&
4947
+ arg.type !== 'qualified_identifier')) {
4948
+ return;
4949
+ }
4950
+ const callerId = this.nodeStack[this.nodeStack.length - 1];
4951
+ if (!callerId)
4952
+ return;
4953
+ const target = (0, c_cpp_1.stripCppTemplateArgs)((0, tree_sitter_helpers_1.getNodeText)(arg, this.source));
4954
+ if (!target || target === localName)
4955
+ return;
4956
+ let locals = this.cppLocalFnPtrs.get(callerId);
4957
+ if (!locals) {
4958
+ locals = new Map();
4959
+ this.cppLocalFnPtrs.set(callerId, locals);
4960
+ }
4961
+ let targets = locals.get(localName);
4962
+ if (!targets) {
4963
+ targets = new Set();
4964
+ locals.set(localName, targets);
4965
+ }
4966
+ targets.add(target);
4967
+ }
4160
4968
  visitFunctionBody(body, _functionId) {
4161
4969
  if (!this.extractor)
4162
4970
  return;
@@ -4172,7 +4980,7 @@ class TreeSitterExtractor {
4172
4980
  if (this.extractor.callTypes.includes(nodeType)) {
4173
4981
  this.extractCall(node);
4174
4982
  }
4175
- else if (INSTANTIATION_KINDS.has(nodeType)) {
4983
+ else if (INSTANTIATION_KINDS.has(nodeType) || this.isVbnetConstructorShapedArrayCreation(node)) {
4176
4984
  // `new Foo()` inside a function body — emit an `instantiates`
4177
4985
  // reference. Without this branch the body walker only knew
4178
4986
  // about `call_expression`, so constructor invocations
@@ -4202,6 +5010,43 @@ class TreeSitterExtractor {
4202
5010
  }
4203
5011
  }
4204
5012
  }
5013
+ // C++ stack / direct-initialization construction — `Calculator calc(0)`
5014
+ // and `Widget w{1, 2}`. Unlike heap `new Calculator(0)` (a new_expression
5015
+ // handled above), these carry the constructor arguments directly on the
5016
+ // declarator with NO call/new node, so the body walker saw no constructor
5017
+ // invocation and recorded no `instantiates` edge (#1035). A declaration's
5018
+ // `type` field IS the constructed class name, so reuse extractInstantiation
5019
+ // (which strips template args / namespace and emits the `instantiates`
5020
+ // ref). Children still recurse below, so a nested ctor-arg call
5021
+ // (`Calculator calc(make())`) keeps its own `calls` ref.
5022
+ if (nodeType === 'declaration' && this.language === 'cpp' && this.isCppStackConstruction(node)) {
5023
+ this.extractInstantiation(node);
5024
+ }
5025
+ // C++ local function-pointer bindings (see cppLocalFnPtrs): record
5026
+ // `auto kernel = &fn<…>;` declarations and `kernel = &other_fn<…>;`
5027
+ // branch reassignments so a call/launch through the local links to the
5028
+ // real target(s). The body walker sees these in source order, and C++
5029
+ // requires declaration-before-use, so the map is always populated before
5030
+ // the call that consumes it.
5031
+ if (this.language === 'cpp' && this.nodeStack.length > 0) {
5032
+ if (nodeType === 'declaration') {
5033
+ for (let i = 0; i < node.namedChildCount; i++) {
5034
+ const child = node.namedChild(i);
5035
+ if (child?.type !== 'init_declarator')
5036
+ continue;
5037
+ const decl = (0, tree_sitter_helpers_1.getChildByField)(child, 'declarator');
5038
+ if (decl?.type !== 'identifier')
5039
+ continue;
5040
+ this.recordCppFnPtrBinding((0, tree_sitter_helpers_1.getNodeText)(decl, this.source), (0, tree_sitter_helpers_1.getChildByField)(child, 'value'));
5041
+ }
5042
+ }
5043
+ else if (nodeType === 'assignment_expression') {
5044
+ const left = (0, tree_sitter_helpers_1.getChildByField)(node, 'left');
5045
+ if (left?.type === 'identifier') {
5046
+ this.recordCppFnPtrBinding((0, tree_sitter_helpers_1.getNodeText)(left, this.source), (0, tree_sitter_helpers_1.getChildByField)(node, 'right'));
5047
+ }
5048
+ }
5049
+ }
4205
5050
  // Static-member / value-read: `Enum.value`, `Type.CONST`, `Foo::BAR`.
4206
5051
  this.extractStaticMemberRef(node);
4207
5052
  // Local variable type annotations inside a body — `const items: Foo[] = []`,
@@ -4396,7 +5241,11 @@ class TreeSitterExtractor {
4396
5241
  }
4397
5242
  // C++ base classes: `class Derived : public Base, private Other` →
4398
5243
  // base_class_clause holds access specifiers + base type(s). Emit an extends
4399
- // ref per base type (skip the public/private/protected keywords).
5244
+ // ref per base type (skip the public/private/protected keywords). A
5245
+ // templated base (`Base<int>`, `ns::Tpl<int>`) arrives as a `template_type`
5246
+ // or a `qualified_identifier` wrapping one; strip the `<…>` args so the ref
5247
+ // matches the bare class the template was defined as — `Base`, `ns::Tpl` —
5248
+ // instead of never resolving (#1043).
4400
5249
  if (child.type === 'base_class_clause') {
4401
5250
  for (const t of child.namedChildren) {
4402
5251
  if (t.type === 'type_identifier' ||
@@ -4404,7 +5253,7 @@ class TreeSitterExtractor {
4404
5253
  t.type === 'template_type') {
4405
5254
  this.unresolvedReferences.push({
4406
5255
  fromNodeId: classId,
4407
- referenceName: (0, tree_sitter_helpers_1.getNodeText)(t, this.source),
5256
+ referenceName: (0, c_cpp_1.stripCppTemplateArgs)((0, tree_sitter_helpers_1.getNodeText)(t, this.source)),
4408
5257
  referenceKind: 'extends',
4409
5258
  line: t.startPosition.row + 1,
4410
5259
  column: t.startPosition.column,
@@ -4522,6 +5371,33 @@ class TreeSitterExtractor {
4522
5371
  }
4523
5372
  }
4524
5373
  }
5374
+ // VB.NET: `Inherits Base` / `Implements IFoo, IBar(Of T)` are STATEMENTS
5375
+ // inside the class body (children of the class node), not header clauses.
5376
+ // Each name is a simple/qualified/generic reference; generics unwrap to
5377
+ // the base identifier and dotted paths keep the trailing segment.
5378
+ if (this.language === 'vbnet' &&
5379
+ (child.type === 'inherits_statement' || child.type === 'implements_statement')) {
5380
+ const kind = child.type === 'inherits_statement' ? 'extends' : 'implements';
5381
+ for (const ref of child.namedChildren) {
5382
+ if (!ref || (ref.type !== 'simple_name' && ref.type !== 'qualified_name' && ref.type !== 'generic_name' && ref.type !== 'global_qualified_name'))
5383
+ continue;
5384
+ let name = (0, tree_sitter_helpers_1.getNodeText)(ref, this.source);
5385
+ name = name.replace(/\(\s*Of\b[^)]*\)/gi, '');
5386
+ const lastDot = name.lastIndexOf('.');
5387
+ if (lastDot >= 0)
5388
+ name = name.slice(lastDot + 1);
5389
+ name = name.trim();
5390
+ if (!name)
5391
+ continue;
5392
+ this.unresolvedReferences.push({
5393
+ fromNodeId: classId,
5394
+ referenceName: name,
5395
+ referenceKind: kind,
5396
+ line: ref.startPosition.row + 1,
5397
+ column: ref.startPosition.column,
5398
+ });
5399
+ }
5400
+ }
4525
5401
  // C#: `class Movie : BaseItem, IPlugin` → base_list with identifier children
4526
5402
  // base_list combines both base class and interfaces in a single colon-separated list.
4527
5403
  // We emit all as 'extends' since the syntax doesn't distinguish them.
@@ -4597,6 +5473,33 @@ class TreeSitterExtractor {
4597
5473
  if (child.type === 'field_declaration_list' || child.type === 'class_heritage') {
4598
5474
  this.extractInheritance(child, classId);
4599
5475
  }
5476
+ // CFML cfscript `component extends="Base" implements="IFoo,IBar" { ... }`
5477
+ // (also covers `interface extends="IBase" { ... }`, which reuses the same
5478
+ // component_attribute shape). Attributes are generic name=value pairs —
5479
+ // (identifier label, expression value) — not a dedicated extends_clause,
5480
+ // so filter by the label text. `implements` is a comma-separated list.
5481
+ if (child.type === 'component_attribute' && node.type === 'component') {
5482
+ const label = child.namedChildren.find((c) => c.type === 'identifier');
5483
+ const value = child.namedChildren.find((c) => c.type !== 'identifier');
5484
+ if (label && value) {
5485
+ const labelText = (0, tree_sitter_helpers_1.getNodeText)(label, this.source).toLowerCase();
5486
+ if (labelText === 'extends' || labelText === 'implements') {
5487
+ const valueText = (0, tree_sitter_helpers_1.getNodeText)(value, this.source).replace(/^["']|["']$/g, '');
5488
+ const names = labelText === 'implements'
5489
+ ? valueText.split(',').map((s) => s.trim()).filter(Boolean)
5490
+ : [valueText.trim()].filter(Boolean);
5491
+ for (const name of names) {
5492
+ this.unresolvedReferences.push({
5493
+ fromNodeId: classId,
5494
+ referenceName: name,
5495
+ referenceKind: labelText === 'implements' ? 'implements' : 'extends',
5496
+ line: value.startPosition.row + 1,
5497
+ column: value.startPosition.column,
5498
+ });
5499
+ }
5500
+ }
5501
+ }
5502
+ }
4600
5503
  }
4601
5504
  }
4602
5505
  /**
@@ -4656,7 +5559,7 @@ class TreeSitterExtractor {
4656
5559
  * Languages that support type annotations (TypeScript, etc.)
4657
5560
  */
4658
5561
  TYPE_ANNOTATION_LANGUAGES = new Set([
4659
- 'typescript', 'tsx', 'dart', 'kotlin', 'swift', 'rust', 'go', 'java', 'csharp', 'scala', 'php',
5562
+ 'typescript', 'tsx', 'arkts', 'dart', 'kotlin', 'swift', 'rust', 'go', 'java', 'csharp', 'scala', 'php',
4660
5563
  ]);
4661
5564
  /**
4662
5565
  * PHP pseudo-types and `self`/`static`/`parent` that aren't project symbols.
@@ -5550,7 +6453,19 @@ function extractFromSource(filePath, source, language, frameworkNames) {
5550
6453
  const extractor = new mybatis_extractor_1.MyBatisExtractor(filePath, source);
5551
6454
  result = extractor.extract();
5552
6455
  }
6456
+ else if (detectedLanguage === 'cfml' || detectedLanguage === 'cfscript') {
6457
+ // Custom extractor for CFML (.cfc/.cfm) — dialect-switches between the
6458
+ // tag-based cfml grammar and the bare-script cfscript grammar. Standalone
6459
+ // `.cfs` files (language 'cfscript') are always pure script (never `<`-led),
6460
+ // so routing them through here too gets them the same anonymous-component
6461
+ // filename fallback as a bare-script `.cfc` — without it a `.cfs` whose
6462
+ // `component { ... }` declares no name (the grammar has no `name` field;
6463
+ // CFML never spells one in source) stays `<anonymous>`.
6464
+ const extractor = new cfml_extractor_1.CfmlExtractor(filePath, source, detectedLanguage);
6465
+ result = extractor.extract();
6466
+ }
5553
6467
  else if (detectedLanguage === 'arkts') {
6468
+ // HomeGraph uses the arkanalyzer-backed ArkTS extractor (not tree-sitter).
5554
6469
  const extractor = new arkts_1.ArkTSExtractor(filePath, source);
5555
6470
  result = extractor.extract();
5556
6471
  }