@optave/codegraph 3.13.0 → 3.15.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 (458) hide show
  1. package/README.md +35 -34
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +38 -40
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/b2.d.ts +7 -0
  6. package/dist/ast-analysis/rules/b2.d.ts.map +1 -0
  7. package/dist/ast-analysis/rules/b2.js +240 -0
  8. package/dist/ast-analysis/rules/b2.js.map +1 -0
  9. package/dist/ast-analysis/rules/b3.d.ts +6 -0
  10. package/dist/ast-analysis/rules/b3.d.ts.map +1 -0
  11. package/dist/ast-analysis/rules/b3.js +105 -0
  12. package/dist/ast-analysis/rules/b3.js.map +1 -0
  13. package/dist/ast-analysis/rules/b4.d.ts +9 -0
  14. package/dist/ast-analysis/rules/b4.d.ts.map +1 -0
  15. package/dist/ast-analysis/rules/b4.js +361 -0
  16. package/dist/ast-analysis/rules/b4.js.map +1 -0
  17. package/dist/ast-analysis/rules/b5.d.ts +4 -0
  18. package/dist/ast-analysis/rules/b5.d.ts.map +1 -0
  19. package/dist/ast-analysis/rules/b5.js +52 -0
  20. package/dist/ast-analysis/rules/b5.js.map +1 -0
  21. package/dist/ast-analysis/rules/c.d.ts +4 -0
  22. package/dist/ast-analysis/rules/c.d.ts.map +1 -0
  23. package/dist/ast-analysis/rules/c.js +143 -0
  24. package/dist/ast-analysis/rules/c.js.map +1 -0
  25. package/dist/ast-analysis/rules/index.d.ts.map +1 -1
  26. package/dist/ast-analysis/rules/index.js +34 -0
  27. package/dist/ast-analysis/rules/index.js.map +1 -1
  28. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  29. package/dist/ast-analysis/rules/javascript.js +3 -0
  30. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  31. package/dist/ast-analysis/shared.d.ts.map +1 -1
  32. package/dist/ast-analysis/shared.js +2 -0
  33. package/dist/ast-analysis/shared.js.map +1 -1
  34. package/dist/ast-analysis/visitor-utils.d.ts +1 -0
  35. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  36. package/dist/ast-analysis/visitor-utils.js +5 -0
  37. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  38. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  39. package/dist/ast-analysis/visitor.js +60 -47
  40. package/dist/ast-analysis/visitor.js.map +1 -1
  41. package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
  42. package/dist/ast-analysis/visitors/cfg-visitor.js +126 -76
  43. package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
  44. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  45. package/dist/ast-analysis/visitors/complexity-visitor.js +27 -15
  46. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  47. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  48. package/dist/ast-analysis/visitors/dataflow-visitor.js +54 -21
  49. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  50. package/dist/cli/commands/config.d.ts.map +1 -1
  51. package/dist/cli/commands/config.js +137 -134
  52. package/dist/cli/commands/config.js.map +1 -1
  53. package/dist/cli/commands/roles.d.ts.map +1 -1
  54. package/dist/cli/commands/roles.js +6 -1
  55. package/dist/cli/commands/roles.js.map +1 -1
  56. package/dist/db/better-sqlite3.d.ts +2 -1
  57. package/dist/db/better-sqlite3.d.ts.map +1 -1
  58. package/dist/db/better-sqlite3.js.map +1 -1
  59. package/dist/db/connection.d.ts +7 -1
  60. package/dist/db/connection.d.ts.map +1 -1
  61. package/dist/db/connection.js +20 -5
  62. package/dist/db/connection.js.map +1 -1
  63. package/dist/db/index.d.ts +1 -1
  64. package/dist/db/index.d.ts.map +1 -1
  65. package/dist/db/index.js +1 -1
  66. package/dist/db/index.js.map +1 -1
  67. package/dist/db/migrations.d.ts.map +1 -1
  68. package/dist/db/migrations.js +68 -0
  69. package/dist/db/migrations.js.map +1 -1
  70. package/dist/db/repository/build-stmts.d.ts.map +1 -1
  71. package/dist/db/repository/build-stmts.js +18 -0
  72. package/dist/db/repository/build-stmts.js.map +1 -1
  73. package/dist/db/repository/dataflow.d.ts +5 -0
  74. package/dist/db/repository/dataflow.d.ts.map +1 -1
  75. package/dist/db/repository/dataflow.js +14 -0
  76. package/dist/db/repository/dataflow.js.map +1 -1
  77. package/dist/db/repository/index.d.ts +1 -1
  78. package/dist/db/repository/index.d.ts.map +1 -1
  79. package/dist/db/repository/index.js +1 -1
  80. package/dist/db/repository/index.js.map +1 -1
  81. package/dist/db/repository/native-repository.d.ts.map +1 -1
  82. package/dist/db/repository/native-repository.js +47 -34
  83. package/dist/db/repository/native-repository.js.map +1 -1
  84. package/dist/domain/analysis/context.d.ts +2 -2
  85. package/dist/domain/analysis/dependencies.d.ts +2 -2
  86. package/dist/domain/analysis/diff-impact.d.ts +2 -2
  87. package/dist/domain/analysis/fn-impact.d.ts +3 -1
  88. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  89. package/dist/domain/analysis/fn-impact.js +4 -0
  90. package/dist/domain/analysis/fn-impact.js.map +1 -1
  91. package/dist/domain/analysis/implementations.d.ts +2 -2
  92. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  93. package/dist/domain/analysis/module-map.js +32 -5
  94. package/dist/domain/analysis/module-map.js.map +1 -1
  95. package/dist/domain/analysis/roles.d.ts +7 -1
  96. package/dist/domain/analysis/roles.d.ts.map +1 -1
  97. package/dist/domain/analysis/roles.js +16 -0
  98. package/dist/domain/analysis/roles.js.map +1 -1
  99. package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
  100. package/dist/domain/graph/builder/call-resolver.d.ts +17 -5
  101. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  102. package/dist/domain/graph/builder/call-resolver.js +85 -220
  103. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  104. package/dist/domain/graph/builder/context.d.ts +1 -0
  105. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  106. package/dist/domain/graph/builder/context.js.map +1 -1
  107. package/dist/domain/graph/builder/helpers.d.ts +16 -1
  108. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  109. package/dist/domain/graph/builder/helpers.js +162 -72
  110. package/dist/domain/graph/builder/helpers.js.map +1 -1
  111. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  112. package/dist/domain/graph/builder/incremental.js +166 -97
  113. package/dist/domain/graph/builder/incremental.js.map +1 -1
  114. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  115. package/dist/domain/graph/builder/pipeline.js +10 -4
  116. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  117. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  118. package/dist/domain/graph/builder/stages/build-edges.js +496 -250
  119. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  120. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  121. package/dist/domain/graph/builder/stages/collect-files.js +10 -7
  122. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  123. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  124. package/dist/domain/graph/builder/stages/detect-changes.js +2 -1
  125. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  126. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  127. package/dist/domain/graph/builder/stages/native-orchestrator.js +895 -545
  128. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  129. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
  130. package/dist/domain/graph/resolver/points-to.js +105 -57
  131. package/dist/domain/graph/resolver/points-to.js.map +1 -1
  132. package/dist/domain/graph/resolver/strategy.d.ts +61 -0
  133. package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
  134. package/dist/domain/graph/resolver/strategy.js +222 -0
  135. package/dist/domain/graph/resolver/strategy.js.map +1 -0
  136. package/dist/domain/graph/watcher.d.ts.map +1 -1
  137. package/dist/domain/graph/watcher.js +16 -9
  138. package/dist/domain/graph/watcher.js.map +1 -1
  139. package/dist/domain/parser.d.ts +12 -0
  140. package/dist/domain/parser.d.ts.map +1 -1
  141. package/dist/domain/parser.js +12 -2
  142. package/dist/domain/parser.js.map +1 -1
  143. package/dist/domain/queries.d.ts +1 -1
  144. package/dist/domain/queries.d.ts.map +1 -1
  145. package/dist/domain/queries.js +1 -1
  146. package/dist/domain/queries.js.map +1 -1
  147. package/dist/domain/wasm-worker-entry.js +3 -0
  148. package/dist/domain/wasm-worker-entry.js.map +1 -1
  149. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  150. package/dist/domain/wasm-worker-pool.js +24 -5
  151. package/dist/domain/wasm-worker-pool.js.map +1 -1
  152. package/dist/domain/wasm-worker-protocol.d.ts +7 -0
  153. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  154. package/dist/extractors/dart.js +48 -3
  155. package/dist/extractors/dart.js.map +1 -1
  156. package/dist/extractors/groovy.js +62 -3
  157. package/dist/extractors/groovy.js.map +1 -1
  158. package/dist/extractors/helpers.d.ts +4 -2
  159. package/dist/extractors/helpers.d.ts.map +1 -1
  160. package/dist/extractors/helpers.js +5 -1
  161. package/dist/extractors/helpers.js.map +1 -1
  162. package/dist/extractors/java.js +77 -1
  163. package/dist/extractors/java.js.map +1 -1
  164. package/dist/extractors/javascript.d.ts.map +1 -1
  165. package/dist/extractors/javascript.js +549 -163
  166. package/dist/extractors/javascript.js.map +1 -1
  167. package/dist/extractors/kotlin.js +58 -3
  168. package/dist/extractors/kotlin.js.map +1 -1
  169. package/dist/extractors/objc.js +25 -2
  170. package/dist/extractors/objc.js.map +1 -1
  171. package/dist/extractors/scala.js +62 -2
  172. package/dist/extractors/scala.js.map +1 -1
  173. package/dist/extractors/swift.js +52 -3
  174. package/dist/extractors/swift.js.map +1 -1
  175. package/dist/features/audit.js +26 -23
  176. package/dist/features/audit.js.map +1 -1
  177. package/dist/features/boundaries.d.ts.map +1 -1
  178. package/dist/features/boundaries.js +12 -9
  179. package/dist/features/boundaries.js.map +1 -1
  180. package/dist/features/cfg.d.ts.map +1 -1
  181. package/dist/features/cfg.js +25 -18
  182. package/dist/features/cfg.js.map +1 -1
  183. package/dist/features/check.d.ts.map +1 -1
  184. package/dist/features/check.js +18 -5
  185. package/dist/features/check.js.map +1 -1
  186. package/dist/features/communities.d.ts +4 -2
  187. package/dist/features/communities.d.ts.map +1 -1
  188. package/dist/features/communities.js +6 -4
  189. package/dist/features/communities.js.map +1 -1
  190. package/dist/features/dataflow.d.ts +60 -0
  191. package/dist/features/dataflow.d.ts.map +1 -1
  192. package/dist/features/dataflow.js +530 -6
  193. package/dist/features/dataflow.js.map +1 -1
  194. package/dist/features/manifesto.d.ts.map +1 -1
  195. package/dist/features/manifesto.js +59 -72
  196. package/dist/features/manifesto.js.map +1 -1
  197. package/dist/features/sequence.d.ts.map +1 -1
  198. package/dist/features/sequence.js +27 -22
  199. package/dist/features/sequence.js.map +1 -1
  200. package/dist/features/snapshot.d.ts.map +1 -1
  201. package/dist/features/snapshot.js +36 -28
  202. package/dist/features/snapshot.js.map +1 -1
  203. package/dist/features/structure.d.ts.map +1 -1
  204. package/dist/features/structure.js +150 -62
  205. package/dist/features/structure.js.map +1 -1
  206. package/dist/features/triage.d.ts.map +1 -1
  207. package/dist/features/triage.js +18 -11
  208. package/dist/features/triage.js.map +1 -1
  209. package/dist/graph/algorithms/bfs.d.ts +1 -1
  210. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  211. package/dist/graph/algorithms/bfs.js +14 -13
  212. package/dist/graph/algorithms/bfs.js.map +1 -1
  213. package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
  214. package/dist/graph/algorithms/tarjan.js +5 -0
  215. package/dist/graph/algorithms/tarjan.js.map +1 -1
  216. package/dist/graph/builders/dependency.js +28 -22
  217. package/dist/graph/builders/dependency.js.map +1 -1
  218. package/dist/graph/classifiers/roles.d.ts +10 -1
  219. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  220. package/dist/graph/classifiers/roles.js +60 -6
  221. package/dist/graph/classifiers/roles.js.map +1 -1
  222. package/dist/infrastructure/config.d.ts +10 -0
  223. package/dist/infrastructure/config.d.ts.map +1 -1
  224. package/dist/infrastructure/config.js +31 -3
  225. package/dist/infrastructure/config.js.map +1 -1
  226. package/dist/infrastructure/registry.d.ts +0 -7
  227. package/dist/infrastructure/registry.d.ts.map +1 -1
  228. package/dist/infrastructure/registry.js +29 -13
  229. package/dist/infrastructure/registry.js.map +1 -1
  230. package/dist/infrastructure/update-check.d.ts.map +1 -1
  231. package/dist/infrastructure/update-check.js +49 -31
  232. package/dist/infrastructure/update-check.js.map +1 -1
  233. package/dist/mcp/server.d.ts +2 -10
  234. package/dist/mcp/server.d.ts.map +1 -1
  235. package/dist/mcp/server.js.map +1 -1
  236. package/dist/mcp/tools/ast-query.d.ts +1 -1
  237. package/dist/mcp/tools/ast-query.d.ts.map +1 -1
  238. package/dist/mcp/tools/audit.d.ts +1 -1
  239. package/dist/mcp/tools/audit.d.ts.map +1 -1
  240. package/dist/mcp/tools/batch-query.d.ts +1 -1
  241. package/dist/mcp/tools/batch-query.d.ts.map +1 -1
  242. package/dist/mcp/tools/branch-compare.d.ts +1 -1
  243. package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
  244. package/dist/mcp/tools/brief.d.ts +1 -1
  245. package/dist/mcp/tools/brief.d.ts.map +1 -1
  246. package/dist/mcp/tools/cfg.d.ts +1 -1
  247. package/dist/mcp/tools/cfg.d.ts.map +1 -1
  248. package/dist/mcp/tools/check.d.ts +1 -1
  249. package/dist/mcp/tools/check.d.ts.map +1 -1
  250. package/dist/mcp/tools/co-changes.d.ts +1 -1
  251. package/dist/mcp/tools/co-changes.d.ts.map +1 -1
  252. package/dist/mcp/tools/code-owners.d.ts +1 -1
  253. package/dist/mcp/tools/code-owners.d.ts.map +1 -1
  254. package/dist/mcp/tools/communities.d.ts +1 -1
  255. package/dist/mcp/tools/communities.d.ts.map +1 -1
  256. package/dist/mcp/tools/complexity.d.ts +1 -1
  257. package/dist/mcp/tools/complexity.d.ts.map +1 -1
  258. package/dist/mcp/tools/context.d.ts +1 -1
  259. package/dist/mcp/tools/context.d.ts.map +1 -1
  260. package/dist/mcp/tools/dataflow.d.ts +1 -1
  261. package/dist/mcp/tools/dataflow.d.ts.map +1 -1
  262. package/dist/mcp/tools/diff-impact.d.ts +1 -1
  263. package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
  264. package/dist/mcp/tools/execution-flow.d.ts +1 -1
  265. package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
  266. package/dist/mcp/tools/export-graph.d.ts +1 -1
  267. package/dist/mcp/tools/export-graph.d.ts.map +1 -1
  268. package/dist/mcp/tools/file-deps.d.ts +1 -1
  269. package/dist/mcp/tools/file-deps.d.ts.map +1 -1
  270. package/dist/mcp/tools/file-exports.d.ts +1 -1
  271. package/dist/mcp/tools/file-exports.d.ts.map +1 -1
  272. package/dist/mcp/tools/find-cycles.d.ts +1 -1
  273. package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
  274. package/dist/mcp/tools/fn-impact.d.ts +1 -1
  275. package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
  276. package/dist/mcp/tools/impact-analysis.d.ts +1 -1
  277. package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
  278. package/dist/mcp/tools/implementations.d.ts +1 -1
  279. package/dist/mcp/tools/implementations.d.ts.map +1 -1
  280. package/dist/mcp/tools/index.d.ts +2 -5
  281. package/dist/mcp/tools/index.d.ts.map +1 -1
  282. package/dist/mcp/tools/index.js.map +1 -1
  283. package/dist/mcp/tools/interfaces.d.ts +1 -1
  284. package/dist/mcp/tools/interfaces.d.ts.map +1 -1
  285. package/dist/mcp/tools/list-functions.d.ts +1 -1
  286. package/dist/mcp/tools/list-functions.d.ts.map +1 -1
  287. package/dist/mcp/tools/list-repos.d.ts +1 -1
  288. package/dist/mcp/tools/list-repos.d.ts.map +1 -1
  289. package/dist/mcp/tools/module-map.d.ts +1 -1
  290. package/dist/mcp/tools/module-map.d.ts.map +1 -1
  291. package/dist/mcp/tools/node-roles.d.ts +1 -1
  292. package/dist/mcp/tools/node-roles.d.ts.map +1 -1
  293. package/dist/mcp/tools/path.d.ts +1 -1
  294. package/dist/mcp/tools/path.d.ts.map +1 -1
  295. package/dist/mcp/tools/query.d.ts +1 -1
  296. package/dist/mcp/tools/query.d.ts.map +1 -1
  297. package/dist/mcp/tools/semantic-search.d.ts +1 -1
  298. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  299. package/dist/mcp/tools/sequence.d.ts +1 -1
  300. package/dist/mcp/tools/sequence.d.ts.map +1 -1
  301. package/dist/mcp/tools/structure.d.ts +1 -1
  302. package/dist/mcp/tools/structure.d.ts.map +1 -1
  303. package/dist/mcp/tools/symbol-children.d.ts +1 -1
  304. package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
  305. package/dist/mcp/tools/triage.d.ts +1 -1
  306. package/dist/mcp/tools/triage.d.ts.map +1 -1
  307. package/dist/mcp/tools/where.d.ts +1 -1
  308. package/dist/mcp/tools/where.d.ts.map +1 -1
  309. package/dist/mcp/types.d.ts +19 -0
  310. package/dist/mcp/types.d.ts.map +1 -0
  311. package/dist/mcp/types.js +6 -0
  312. package/dist/mcp/types.js.map +1 -0
  313. package/dist/presentation/queries-cli/index.d.ts +1 -1
  314. package/dist/presentation/queries-cli/index.d.ts.map +1 -1
  315. package/dist/presentation/queries-cli/index.js +1 -1
  316. package/dist/presentation/queries-cli/index.js.map +1 -1
  317. package/dist/presentation/queries-cli/overview.d.ts +1 -0
  318. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  319. package/dist/presentation/queries-cli/overview.js +20 -1
  320. package/dist/presentation/queries-cli/overview.js.map +1 -1
  321. package/dist/presentation/queries-cli.d.ts +1 -1
  322. package/dist/presentation/queries-cli.d.ts.map +1 -1
  323. package/dist/presentation/queries-cli.js +1 -1
  324. package/dist/presentation/queries-cli.js.map +1 -1
  325. package/dist/presentation/viewer.d.ts.map +1 -1
  326. package/dist/presentation/viewer.js +45 -32
  327. package/dist/presentation/viewer.js.map +1 -1
  328. package/dist/shared/constants.d.ts +21 -0
  329. package/dist/shared/constants.d.ts.map +1 -1
  330. package/dist/shared/constants.js +25 -0
  331. package/dist/shared/constants.js.map +1 -1
  332. package/dist/shared/normalize.d.ts.map +1 -1
  333. package/dist/shared/normalize.js +12 -22
  334. package/dist/shared/normalize.js.map +1 -1
  335. package/dist/shared/paginate.d.ts +4 -17
  336. package/dist/shared/paginate.d.ts.map +1 -1
  337. package/dist/shared/paginate.js.map +1 -1
  338. package/dist/types.d.ts +76 -1
  339. package/dist/types.d.ts.map +1 -1
  340. package/grammars/tree-sitter-erlang.wasm +0 -0
  341. package/package.json +7 -7
  342. package/src/ast-analysis/engine.ts +43 -63
  343. package/src/ast-analysis/rules/b2.ts +263 -0
  344. package/src/ast-analysis/rules/b3.ts +127 -0
  345. package/src/ast-analysis/rules/b4.ts +378 -0
  346. package/src/ast-analysis/rules/b5.ts +65 -0
  347. package/src/ast-analysis/rules/c.ts +157 -0
  348. package/src/ast-analysis/rules/index.ts +34 -0
  349. package/src/ast-analysis/rules/javascript.ts +3 -0
  350. package/src/ast-analysis/shared.ts +2 -0
  351. package/src/ast-analysis/visitor-utils.ts +5 -0
  352. package/src/ast-analysis/visitor.ts +82 -52
  353. package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
  354. package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
  355. package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
  356. package/src/cli/commands/config.ts +184 -184
  357. package/src/cli/commands/roles.ts +6 -1
  358. package/src/db/better-sqlite3.ts +5 -4
  359. package/src/db/connection.ts +23 -5
  360. package/src/db/index.ts +1 -0
  361. package/src/db/migrations.ts +68 -0
  362. package/src/db/repository/build-stmts.ts +30 -0
  363. package/src/db/repository/dataflow.ts +16 -0
  364. package/src/db/repository/index.ts +1 -1
  365. package/src/db/repository/native-repository.ts +56 -40
  366. package/src/domain/analysis/fn-impact.ts +4 -0
  367. package/src/domain/analysis/module-map.ts +38 -6
  368. package/src/domain/analysis/roles.ts +23 -0
  369. package/src/domain/graph/builder/call-resolver.ts +112 -232
  370. package/src/domain/graph/builder/context.ts +1 -0
  371. package/src/domain/graph/builder/helpers.ts +190 -72
  372. package/src/domain/graph/builder/incremental.ts +249 -120
  373. package/src/domain/graph/builder/pipeline.ts +11 -5
  374. package/src/domain/graph/builder/stages/build-edges.ts +696 -296
  375. package/src/domain/graph/builder/stages/collect-files.ts +12 -6
  376. package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
  377. package/src/domain/graph/builder/stages/native-orchestrator.ts +1102 -590
  378. package/src/domain/graph/resolver/points-to.ts +182 -59
  379. package/src/domain/graph/resolver/strategy.ts +265 -0
  380. package/src/domain/graph/watcher.ts +19 -9
  381. package/src/domain/parser.ts +12 -2
  382. package/src/domain/queries.ts +1 -1
  383. package/src/domain/wasm-worker-entry.ts +3 -0
  384. package/src/domain/wasm-worker-pool.ts +28 -4
  385. package/src/domain/wasm-worker-protocol.ts +4 -0
  386. package/src/extractors/dart.ts +48 -3
  387. package/src/extractors/groovy.ts +62 -2
  388. package/src/extractors/helpers.ts +5 -2
  389. package/src/extractors/java.ts +80 -1
  390. package/src/extractors/javascript.ts +566 -161
  391. package/src/extractors/kotlin.ts +57 -3
  392. package/src/extractors/objc.ts +25 -1
  393. package/src/extractors/scala.ts +63 -1
  394. package/src/extractors/swift.ts +46 -3
  395. package/src/features/audit.ts +43 -34
  396. package/src/features/boundaries.ts +17 -9
  397. package/src/features/cfg.ts +31 -22
  398. package/src/features/check.ts +21 -5
  399. package/src/features/communities.ts +28 -19
  400. package/src/features/dataflow.ts +755 -6
  401. package/src/features/manifesto.ts +76 -75
  402. package/src/features/sequence.ts +29 -23
  403. package/src/features/snapshot.ts +36 -25
  404. package/src/features/structure.ts +185 -55
  405. package/src/features/triage.ts +28 -15
  406. package/src/graph/algorithms/bfs.ts +13 -12
  407. package/src/graph/algorithms/tarjan.ts +5 -0
  408. package/src/graph/builders/dependency.ts +35 -23
  409. package/src/graph/classifiers/roles.ts +74 -7
  410. package/src/infrastructure/config.ts +32 -3
  411. package/src/infrastructure/registry.ts +44 -20
  412. package/src/infrastructure/update-check.ts +55 -33
  413. package/src/mcp/server.ts +2 -8
  414. package/src/mcp/tools/ast-query.ts +1 -1
  415. package/src/mcp/tools/audit.ts +1 -1
  416. package/src/mcp/tools/batch-query.ts +1 -1
  417. package/src/mcp/tools/branch-compare.ts +1 -1
  418. package/src/mcp/tools/brief.ts +1 -1
  419. package/src/mcp/tools/cfg.ts +1 -1
  420. package/src/mcp/tools/check.ts +1 -1
  421. package/src/mcp/tools/co-changes.ts +1 -1
  422. package/src/mcp/tools/code-owners.ts +1 -1
  423. package/src/mcp/tools/communities.ts +1 -1
  424. package/src/mcp/tools/complexity.ts +1 -1
  425. package/src/mcp/tools/context.ts +1 -1
  426. package/src/mcp/tools/dataflow.ts +1 -1
  427. package/src/mcp/tools/diff-impact.ts +1 -1
  428. package/src/mcp/tools/execution-flow.ts +1 -1
  429. package/src/mcp/tools/export-graph.ts +1 -1
  430. package/src/mcp/tools/file-deps.ts +1 -1
  431. package/src/mcp/tools/file-exports.ts +1 -1
  432. package/src/mcp/tools/find-cycles.ts +1 -1
  433. package/src/mcp/tools/fn-impact.ts +1 -1
  434. package/src/mcp/tools/impact-analysis.ts +1 -1
  435. package/src/mcp/tools/implementations.ts +1 -1
  436. package/src/mcp/tools/index.ts +2 -5
  437. package/src/mcp/tools/interfaces.ts +1 -1
  438. package/src/mcp/tools/list-functions.ts +1 -1
  439. package/src/mcp/tools/list-repos.ts +1 -1
  440. package/src/mcp/tools/module-map.ts +1 -1
  441. package/src/mcp/tools/node-roles.ts +1 -1
  442. package/src/mcp/tools/path.ts +1 -1
  443. package/src/mcp/tools/query.ts +1 -1
  444. package/src/mcp/tools/semantic-search.ts +1 -1
  445. package/src/mcp/tools/sequence.ts +1 -1
  446. package/src/mcp/tools/structure.ts +1 -1
  447. package/src/mcp/tools/symbol-children.ts +1 -1
  448. package/src/mcp/tools/triage.ts +1 -1
  449. package/src/mcp/tools/where.ts +1 -1
  450. package/src/mcp/types.ts +21 -0
  451. package/src/presentation/queries-cli/index.ts +1 -1
  452. package/src/presentation/queries-cli/overview.ts +35 -1
  453. package/src/presentation/queries-cli.ts +1 -0
  454. package/src/presentation/viewer.ts +98 -87
  455. package/src/shared/constants.ts +26 -0
  456. package/src/shared/normalize.ts +13 -22
  457. package/src/shared/paginate.ts +4 -18
  458. package/src/types.ts +86 -1
@@ -6,6 +6,7 @@ import type {
6
6
  CallAssignment,
7
7
  ClassRelation,
8
8
  Definition,
9
+ DynamicKind,
9
10
  Export,
10
11
  ExtractorOutput,
11
12
  FnRefBinding,
@@ -96,6 +97,13 @@ const BUILTIN_GLOBALS: Set<string> = new Set([
96
97
  const MAX_PROPAGATION_DEPTH = 3;
97
98
  /** Confidence penalty applied per propagation hop (1.0 → 0.9 → 0.8 → 0.7). */
98
99
  export const PROPAGATION_HOP_PENALTY = 0.1;
100
+ /**
101
+ * Confidence score for a return type inferred from `return new Constructor()` with no
102
+ * explicit TypeScript annotation. Registered as `analysis.typeInferenceConfidence` in
103
+ * `src/infrastructure/config.ts` DEFAULTS — kept in sync manually until config is
104
+ * threaded through to `extractSymbols`.
105
+ */
106
+ const INFERRED_RETURN_TYPE_CONFIDENCE = 0.85;
99
107
 
100
108
  /**
101
109
  * Extract symbols from a JS/TS parsed AST.
@@ -304,12 +312,14 @@ function dispatchQueryMatch(
304
312
  } else if (c.exp_node) {
305
313
  handleExportCapture(c, exps, imports);
306
314
  } else if (c.callfn_node) {
307
- calls.push({
308
- name: c.callfn_name!.text,
309
- line: nodeStartLine(c.callfn_node),
310
- });
315
+ // Route through extractCallInfo so special identifier calls (eval) get classified.
316
+ const callfnInfo = extractCallInfo(c.callfn_name!, c.callfn_node);
317
+ if (callfnInfo) calls.push(callfnInfo);
311
318
  calls.push(...extractCallbackReferenceCalls(c.callfn_node));
312
319
  } else if (c.callmem_node) {
320
+ // extractCallInfo → extractMemberExprCallInfo applies the plain-identifier guard for
321
+ // .call/.apply/.bind: when the object is a bare identifier (e.g. `fn.call(ctx)`),
322
+ // the call is emitted as static (no dynamic flag), matching the walk path and native engine.
313
323
  const callInfo = extractCallInfo(c.callmem_fn!, c.callmem_node);
314
324
  if (callInfo) calls.push(callInfo);
315
325
  const cbDef = extractCallbackDefinition(c.callmem_node, c.callmem_fn);
@@ -320,10 +330,20 @@ function dispatchQueryMatch(
320
330
  if (callInfo) calls.push(callInfo);
321
331
  calls.push(...extractCallbackReferenceCalls(c.callsub_node));
322
332
  } else if (c.newfn_node) {
323
- calls.push({
324
- name: c.newfn_name!.text,
325
- line: nodeStartLine(c.newfn_node),
326
- });
333
+ if (c.newfn_name!.text === 'Function') {
334
+ // new Function(body) — dynamic code execution; classify as eval kind
335
+ calls.push({
336
+ name: '<dynamic:eval>',
337
+ line: nodeStartLine(c.newfn_node),
338
+ dynamic: true,
339
+ dynamicKind: 'eval',
340
+ });
341
+ } else {
342
+ calls.push({
343
+ name: c.newfn_name!.text,
344
+ line: nodeStartLine(c.newfn_node),
345
+ });
346
+ }
327
347
  } else if (c.newmem_node) {
328
348
  const callInfo = extractCallInfo(c.newmem_fn!, c.newmem_node);
329
349
  if (callInfo) calls.push(callInfo);
@@ -382,8 +402,11 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
382
402
  arrayCallbackBindings,
383
403
  });
384
404
 
385
- // Extract definitions from destructured bindings (query patterns don't match object_pattern)
386
- extractDestructuredBindingsWalk(tree.rootNode, definitions);
405
+ // Extract definitions from destructured bindings (query patterns don't match object_pattern).
406
+ // Also collects CJS require bindings (const { X } = require('…')) into a separate list so
407
+ // importedNames can classify them as import artifacts without creating DB edges (#1661).
408
+ const cjsRequireBindings: Array<{ names: string[]; source: string }> = [];
409
+ extractDestructuredBindingsWalk(tree.rootNode, definitions, cjsRequireBindings);
387
410
 
388
411
  // Everything without bespoke traversal semantics is collected in ONE pass:
389
412
  // dynamic import() calls, prototype-method definitions, param bindings,
@@ -426,6 +449,7 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
426
449
  thisCallBindings,
427
450
  newExpressions,
428
451
  ...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
452
+ ...(cjsRequireBindings.length > 0 ? { cjsRequireBindings } : {}),
429
453
  };
430
454
  }
431
455
 
@@ -474,6 +498,7 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
474
498
  }
475
499
 
476
500
  extractConstDeclarators(declNode, definitions);
501
+ extractLetVarObjLiteralDeclarators(declNode, definitions);
477
502
 
478
503
  // Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
479
504
  if (child.type !== 'export_statement') {
@@ -490,8 +515,15 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
490
515
  /**
491
516
  * Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
492
517
  * e.g. `const { handleToken, checkPermissions } = initAuth(config)`
518
+ *
519
+ * When `cjsRequireBindings` is provided, also records `const { X } = require('./path')` patterns
520
+ * so the edge builder can classify X as an import artifact rather than a local definition (#1661).
493
521
  */
494
- function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Definition[]): void {
522
+ function extractDestructuredBindingsWalk(
523
+ node: TreeSitterNode,
524
+ definitions: Definition[],
525
+ cjsRequireBindings?: Array<{ names: string[]; source: string }>,
526
+ ): void {
495
527
  for (let i = 0; i < node.childCount; i++) {
496
528
  const child = node.child(i);
497
529
  if (!child) continue;
@@ -519,6 +551,43 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
519
551
  nodeEndLine(declNode),
520
552
  definitions,
521
553
  );
554
+ // Record CJS require bindings so importedNames can classify these names
555
+ // as import artifacts, preventing false local-definition blocking (#1661).
556
+ if (cjsRequireBindings) {
557
+ const valueN = declarator.childForFieldName('value');
558
+ if (valueN?.type === 'call_expression') {
559
+ const fn = valueN.childForFieldName('function');
560
+ if (fn?.text === 'require') {
561
+ const args = valueN.childForFieldName('arguments');
562
+ const strArg = args && findChild(args, 'string');
563
+ if (strArg) {
564
+ const modPath = strArg.text.replace(/['"]/g, '');
565
+ const names: string[] = [];
566
+ for (let k = 0; k < nameN.childCount; k++) {
567
+ const prop = nameN.child(k);
568
+ if (!prop) continue;
569
+ if (
570
+ prop.type === 'shorthand_property_identifier_pattern' ||
571
+ prop.type === 'shorthand_property_identifier'
572
+ ) {
573
+ names.push(prop.text);
574
+ } else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
575
+ const val = prop.childForFieldName('value');
576
+ if (
577
+ val?.type === 'identifier' ||
578
+ val?.type === 'shorthand_property_identifier_pattern'
579
+ ) {
580
+ names.push(val.text);
581
+ }
582
+ }
583
+ }
584
+ if (names.length > 0) {
585
+ cjsRequireBindings.push({ names, source: modPath });
586
+ }
587
+ }
588
+ }
589
+ }
590
+ }
522
591
  } else if (nameN && nameN.type === 'array_pattern') {
523
592
  // `const [x, y] = ...` — emit a single constant node whose name is the
524
593
  // full array pattern text (e.g. `[x, y]`), matching native engine behaviour.
@@ -533,7 +602,7 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
533
602
  }
534
603
 
535
604
  if (child.type !== 'export_statement') {
536
- extractDestructuredBindingsWalk(child, definitions);
605
+ extractDestructuredBindingsWalk(child, definitions, cjsRequireBindings);
537
606
  }
538
607
  }
539
608
  }
@@ -578,6 +647,34 @@ function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definiti
578
647
  }
579
648
  }
580
649
 
650
+ /**
651
+ * Extract qualified method definitions from `let`/`var` object-literal declarations.
652
+ * Mirrors `match_js_objlit_qualified_method_defs` in `javascript.rs`, which emits
653
+ * qualified definitions for `method_definition` (all declaration kinds) and
654
+ * `pair+arrow/function` (`let`/`var` only, since `const` is already handled by
655
+ * `extractConstDeclarators` → `extractObjectLiteralFunctions`).
656
+ *
657
+ * Called from extractConstantsWalk which already provides the function-scope guard.
658
+ * `var q1 = { m1() {} }` → emits Definition { name: 'q1.m1', kind: 'function' }
659
+ */
660
+ function extractLetVarObjLiteralDeclarators(
661
+ declNode: TreeSitterNode,
662
+ definitions: Definition[],
663
+ ): void {
664
+ const t = declNode.type;
665
+ if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
666
+ if (declNode.text.startsWith('const ')) return; // handled by extractConstDeclarators
667
+
668
+ for (let j = 0; j < declNode.childCount; j++) {
669
+ const declarator = declNode.child(j);
670
+ if (declarator?.type !== 'variable_declarator') continue;
671
+ const nameN = declarator.childForFieldName('name');
672
+ const valueN = declarator.childForFieldName('value');
673
+ if (nameN?.type !== 'identifier' || !valueN || valueN.type !== 'object') continue;
674
+ extractObjectLiteralFunctions(valueN, nameN.text, definitions);
675
+ }
676
+ }
677
+
581
678
  /**
582
679
  * Recursive walk to find dynamic import() calls.
583
680
  * Query patterns match call_expression with identifier/member_expression/subscript_expression
@@ -770,6 +867,9 @@ function walkJavaScriptNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
770
867
  case 'enum_declaration':
771
868
  handleEnumDecl(node, ctx);
772
869
  break;
870
+ case 'decorator':
871
+ handleDecorator(node, ctx.calls);
872
+ break;
773
873
  case 'call_expression':
774
874
  handleCallExpr(node, ctx);
775
875
  break;
@@ -1036,6 +1136,18 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
1036
1136
  if (valueN.type === 'object') {
1037
1137
  extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
1038
1138
  }
1139
+ } else if (
1140
+ !isConst &&
1141
+ nameN.type === 'identifier' &&
1142
+ valueN.type === 'object' &&
1143
+ !hasFunctionScopeAncestor(node)
1144
+ ) {
1145
+ // `let`/`var` object literals: extract qualified method definitions so that
1146
+ // `obj.method()` calls resolve correctly. Mirrors Rust match_js_objlit_qualified_method_defs
1147
+ // which emits method_definition qualified names for ALL declaration kinds and
1148
+ // pair+arrow/function for let/var only (const is already handled above).
1149
+ // Scope guard prevents local object properties from polluting the global index.
1150
+ extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
1039
1151
  } else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
1040
1152
  // Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
1041
1153
  // Each destructured property becomes a function definition so it can be
@@ -1050,6 +1162,40 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
1050
1162
  nodeEndLine(node),
1051
1163
  ctx.definitions,
1052
1164
  );
1165
+ // Record CJS require bindings for import-artifact classification (#1661).
1166
+ if (valueN?.type === 'call_expression') {
1167
+ const fn = valueN.childForFieldName('function');
1168
+ if (fn?.text === 'require') {
1169
+ const args = valueN.childForFieldName('arguments');
1170
+ const strArg = args && findChild(args, 'string');
1171
+ if (strArg) {
1172
+ const modPath = strArg.text.replace(/['"]/g, '');
1173
+ const names: string[] = [];
1174
+ for (let k = 0; k < nameN.childCount; k++) {
1175
+ const prop = nameN.child(k);
1176
+ if (!prop) continue;
1177
+ if (
1178
+ prop.type === 'shorthand_property_identifier_pattern' ||
1179
+ prop.type === 'shorthand_property_identifier'
1180
+ ) {
1181
+ names.push(prop.text);
1182
+ } else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
1183
+ const val = prop.childForFieldName('value');
1184
+ if (
1185
+ val?.type === 'identifier' ||
1186
+ val?.type === 'shorthand_property_identifier_pattern'
1187
+ ) {
1188
+ names.push(val.text);
1189
+ }
1190
+ }
1191
+ }
1192
+ if (names.length > 0) {
1193
+ if (!ctx.cjsRequireBindings) ctx.cjsRequireBindings = [];
1194
+ ctx.cjsRequireBindings.push({ names, source: modPath });
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1053
1199
  } else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
1054
1200
  // Array destructuring: `const [x, y] = ...` — emit a single constant node
1055
1201
  // whose name is the full array pattern text (e.g. `[x, y]`), matching
@@ -1213,13 +1359,54 @@ function handleNewExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
1213
1359
  const ctor = node.childForFieldName('constructor') || node.child(1);
1214
1360
  if (!ctor) return;
1215
1361
  if (ctor.type === 'identifier') {
1216
- ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
1362
+ if (ctor.text === 'Function') {
1363
+ // new Function(body) — dynamic code execution; undecidable static target
1364
+ ctx.calls.push({
1365
+ name: '<dynamic:eval>',
1366
+ line: nodeStartLine(node),
1367
+ dynamic: true,
1368
+ dynamicKind: 'eval' as DynamicKind,
1369
+ });
1370
+ } else {
1371
+ ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
1372
+ }
1217
1373
  } else if (ctor.type === 'member_expression') {
1218
1374
  const callInfo = extractCallInfo(ctor, node);
1219
1375
  if (callInfo) ctx.calls.push(callInfo);
1220
1376
  }
1221
1377
  }
1222
1378
 
1379
+ /**
1380
+ * Handle a TypeScript/JS decorator node.
1381
+ *
1382
+ * Only handles bare-identifier and bare-member-expression decorators
1383
+ * (`@Foo`, `@Foo.bar`) since decorated call expressions (`@Foo()`, `@Foo.bar()`)
1384
+ * are already visited as `call_expression` children by the recursive walker.
1385
+ */
1386
+ function handleDecorator(node: TreeSitterNode, calls: Call[]): void {
1387
+ // Decorators wrap their expression; find the first non-@ child
1388
+ for (let i = 0; i < node.childCount; i++) {
1389
+ const child = node.child(i);
1390
+ if (!child || child.type === '@') continue;
1391
+ const t = child.type;
1392
+ if (t === 'identifier') {
1393
+ // @Foo — the identifier is the decorator factory; emit as reflection call
1394
+ calls.push({
1395
+ name: child.text,
1396
+ line: nodeStartLine(node),
1397
+ dynamic: true,
1398
+ dynamicKind: 'reflection',
1399
+ });
1400
+ } else if (t === 'member_expression') {
1401
+ // @Foo.bar — emit as reflection; always mark dynamic since it's decorator dispatch
1402
+ const callInfo = extractCallInfo(child, node);
1403
+ if (callInfo) calls.push({ ...callInfo, dynamic: true, dynamicKind: 'reflection' });
1404
+ }
1405
+ // call_expression / other — handled by the recursive walker automatically
1406
+ break;
1407
+ }
1408
+ }
1409
+
1223
1410
  /** Handle a dynamic import() call expression and add to imports if static. */
1224
1411
  function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
1225
1412
  const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
@@ -1592,8 +1779,8 @@ function storeReturnType(
1592
1779
  const inferred = findReturnNewExprType(body);
1593
1780
  if (inferred) {
1594
1781
  const existing = returnTypeMap.get(fnName);
1595
- if (!existing || 0.85 > existing.confidence)
1596
- returnTypeMap.set(fnName, { type: inferred, confidence: 0.85 });
1782
+ if (!existing || INFERRED_RETURN_TYPE_CONFIDENCE > existing.confidence)
1783
+ returnTypeMap.set(fnName, { type: inferred, confidence: INFERRED_RETURN_TYPE_CONFIDENCE });
1597
1784
  }
1598
1785
  }
1599
1786
  }
@@ -1967,7 +2154,190 @@ function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollector
1967
2154
  walk(rootNode, 0, null, null);
1968
2155
  }
1969
2156
 
1970
- /** Extract type info from a variable_declarator: type annotation, constructor, or factory. */
2157
+ /**
2158
+ * Record function-reference bindings from a variable_declarator's value node.
2159
+ *
2160
+ * Captures three patterns (Phase 8.3):
2161
+ * - `const fn = handler` (identifier alias)
2162
+ * - `const fn = obj.method` (member_expression alias)
2163
+ * - `const f = fn.bind(ctx)` (bind creates a bound alias)
2164
+ *
2165
+ * Must be called before any type-analysis early returns so every declarator
2166
+ * contributes to fnRefBindings regardless of whether it has a type annotation.
2167
+ */
2168
+ function collectFnRefBindings(
2169
+ lhsName: string,
2170
+ valueN: TreeSitterNode,
2171
+ fnRefBindings: FnRefBinding[],
2172
+ ): void {
2173
+ if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
2174
+ fnRefBindings.push({ lhs: lhsName, rhs: valueN.text });
2175
+ return;
2176
+ }
2177
+ if (valueN.type === 'member_expression') {
2178
+ const prop = valueN.childForFieldName('property');
2179
+ const obj = valueN.childForFieldName('object');
2180
+ // Guard: only static property access (property_identifier or identifier), not
2181
+ // computed subscript expressions like obj[expr] where prop.text would be the
2182
+ // full expression rather than a simple name — those can never match pts keys.
2183
+ if (
2184
+ prop &&
2185
+ (prop.type === 'property_identifier' || prop.type === 'identifier') &&
2186
+ obj?.type === 'identifier' &&
2187
+ !BUILTIN_GLOBALS.has(obj.text)
2188
+ ) {
2189
+ fnRefBindings.push({ lhs: lhsName, rhs: prop.text, rhsReceiver: obj.text });
2190
+ }
2191
+ return;
2192
+ }
2193
+ if (valueN.type === 'call_expression') {
2194
+ // `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
2195
+ // pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
2196
+ // Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
2197
+ // binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
2198
+ const callFn = valueN.childForFieldName('function');
2199
+ if (callFn?.type === 'member_expression') {
2200
+ const bindProp = callFn.childForFieldName('property');
2201
+ if (bindProp?.text === 'bind') {
2202
+ const boundFn = callFn.childForFieldName('object');
2203
+ if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
2204
+ fnRefBindings.push({ lhs: lhsName, rhs: boundFn.text });
2205
+ }
2206
+ }
2207
+ }
2208
+ }
2209
+ }
2210
+
2211
+ /**
2212
+ * Handle the `call_expression` branch of variable_declarator type-map seeding.
2213
+ *
2214
+ * Processes three sub-cases in priority order:
2215
+ * 1. Object.create({ ... }) — seeds composite pts keys from the prototype object (Phase 8.3e)
2216
+ * 2. Inter-procedural return-type propagation via returnTypeMap (Phase 8.2)
2217
+ * 3. Factory method heuristic: `const x = Foo.create()` → type Foo at confidence 0.7
2218
+ */
2219
+ function handleCallExprTypeMap(
2220
+ lhsName: string,
2221
+ valueN: TreeSitterNode,
2222
+ typeMap: Map<string, TypeMapEntry>,
2223
+ returnTypeMap: Map<string, TypeMapEntry> | undefined,
2224
+ callAssignments: CallAssignment[] | undefined,
2225
+ ): void {
2226
+ const createFn = valueN.childForFieldName('function');
2227
+ // Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
2228
+ if (createFn?.type === 'member_expression') {
2229
+ const createObj = createFn.childForFieldName('object');
2230
+ const createProp = createFn.childForFieldName('property');
2231
+ if (createObj?.text === 'Object' && createProp?.text === 'create') {
2232
+ const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
2233
+ if (createArgs) {
2234
+ let proto: TreeSitterNode | null = null;
2235
+ for (let i = 0; i < createArgs.childCount; i++) {
2236
+ const n = createArgs.child(i);
2237
+ if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
2238
+ proto = n;
2239
+ break;
2240
+ }
2241
+ }
2242
+ if (proto?.type === 'object') {
2243
+ seedProtoProperties(lhsName, proto, typeMap);
2244
+ }
2245
+ }
2246
+ return;
2247
+ }
2248
+ }
2249
+ // Phase 8.2: inter-procedural propagation — try to resolve return type from
2250
+ // the local returnTypeMap before falling back to factory heuristics.
2251
+ if (returnTypeMap) {
2252
+ const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
2253
+ if (result) {
2254
+ setTypeMapEntry(typeMap, lhsName, result.type, result.confidence);
2255
+ return;
2256
+ }
2257
+ }
2258
+ // Record for cross-file resolution in build-edges.ts (imported functions)
2259
+ if (callAssignments) {
2260
+ recordCallAssignment(valueN, lhsName, typeMap, callAssignments);
2261
+ }
2262
+ // Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
2263
+ if (createFn?.type === 'member_expression') {
2264
+ const obj = createFn.childForFieldName('object');
2265
+ if (obj?.type === 'identifier') {
2266
+ const objName = obj.text;
2267
+ if (objName[0] && objName[0] !== objName[0].toLowerCase() && !BUILTIN_GLOBALS.has(objName)) {
2268
+ setTypeMapEntry(typeMap, lhsName, objName, 0.7);
2269
+ }
2270
+ }
2271
+ }
2272
+ }
2273
+
2274
+ /**
2275
+ * Seed composite pts keys from a module-level object literal assignment (Phase 8.3f).
2276
+ *
2277
+ * `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
2278
+ * `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
2279
+ * `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
2280
+ * `const obj = { baz() {} }` (method shorthand) → typeMap['obj.baz'] = 'obj.baz'
2281
+ *
2282
+ * For function/arrow values, the value is the qualified name ('obj.baz') because
2283
+ * extractObjectLiteralFunctions registers definitions under that qualified name to avoid
2284
+ * polluting the global index with bare property names like 'init', 'run', or 'render'.
2285
+ * Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
2286
+ * resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
2287
+ *
2288
+ * Scope guard: caller must ensure `node` is not inside a function body
2289
+ * (mirrors Rust handle_var_decl's find_parent_of_types check — function-scoped
2290
+ * `const localObj = { fn: ... }` must not shadow a module-level `const obj`).
2291
+ */
2292
+ function handleObjectLiteralTypeMap(
2293
+ lhsName: string,
2294
+ valueN: TreeSitterNode,
2295
+ typeMap: Map<string, TypeMapEntry>,
2296
+ ): void {
2297
+ for (let i = 0; i < valueN.childCount; i++) {
2298
+ const child = valueN.child(i);
2299
+ if (!child) continue;
2300
+ if (child.type === 'shorthand_property_identifier') {
2301
+ setTypeMapEntry(typeMap, `${lhsName}.${child.text}`, child.text, 0.85);
2302
+ } else if (child.type === 'pair') {
2303
+ const keyNode = child.childForFieldName('key');
2304
+ const valNode = child.childForFieldName('value');
2305
+ if (!keyNode || !valNode) continue;
2306
+ const keyName =
2307
+ keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
2308
+ if (!keyName) continue;
2309
+ const qualifiedKey = `${lhsName}.${keyName}`;
2310
+ if (
2311
+ valNode.type === 'arrow_function' ||
2312
+ valNode.type === 'function_expression' ||
2313
+ valNode.type === 'function'
2314
+ ) {
2315
+ // Store the qualified name so the resolver finds the qualified definition.
2316
+ setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
2317
+ } else if (valNode.type === 'identifier') {
2318
+ setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
2319
+ }
2320
+ } else if (child.type === 'method_definition') {
2321
+ // Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
2322
+ // extractObjectLiteralFunctions registers a definition under the qualified name;
2323
+ // seed the matching typeMap entry so the two-step accessor dispatch finds it.
2324
+ const nameNode = child.childForFieldName('name');
2325
+ if (!nameNode) continue;
2326
+ setTypeMapEntry(typeMap, `${lhsName}.${nameNode.text}`, `${lhsName}.${nameNode.text}`, 0.85);
2327
+ }
2328
+ }
2329
+ }
2330
+
2331
+ /**
2332
+ * Extract type info from a variable_declarator: type annotation, constructor, or factory.
2333
+ *
2334
+ * Orchestrates four concerns in priority order:
2335
+ * 1. fnRefBindings — always collected first (before any early return)
2336
+ * 2. new_expression — constructor wins over annotation (runtime type is authoritative)
2337
+ * 3. type_annotation — confidence 0.9 for static analysis
2338
+ * 4. call_expression / object literal — delegated to handleCallExprTypeMap /
2339
+ * handleObjectLiteralTypeMap
2340
+ */
1971
2341
  function handleVarDeclaratorTypeMap(
1972
2342
  node: TreeSitterNode,
1973
2343
  typeMap: Map<string, TypeMapEntry>,
@@ -1981,48 +2351,12 @@ function handleVarDeclaratorTypeMap(
1981
2351
  const typeAnno = findChild(node, 'type_annotation');
1982
2352
  const valueN = node.childForFieldName('value');
1983
2353
 
1984
- // Phase 8.3: record function-reference bindings before any type-analysis early returns.
1985
- // Captures `const fn = handler` (identifier) and `const fn = obj.method` (member_expression).
1986
- // Also handles `const f = fn.bind(ctx)` — bind returns a new function aliasing fn.
2354
+ // 1. fnRefBindings must run before any early return so every declarator contributes.
1987
2355
  if (fnRefBindings && valueN) {
1988
- if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
1989
- fnRefBindings.push({ lhs: nameN.text, rhs: valueN.text });
1990
- } else if (valueN.type === 'member_expression') {
1991
- const prop = valueN.childForFieldName('property');
1992
- const obj = valueN.childForFieldName('object');
1993
- // Guard: only static property access (property_identifier or identifier), not
1994
- // computed subscript expressions like obj[expr] where prop.text would be the
1995
- // full expression rather than a simple name — those can never match pts keys.
1996
- if (
1997
- prop &&
1998
- (prop.type === 'property_identifier' || prop.type === 'identifier') &&
1999
- obj?.type === 'identifier' &&
2000
- !BUILTIN_GLOBALS.has(obj.text)
2001
- ) {
2002
- fnRefBindings.push({ lhs: nameN.text, rhs: prop.text, rhsReceiver: obj.text });
2003
- }
2004
- } else if (valueN.type === 'call_expression') {
2005
- // `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
2006
- // pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
2007
- // Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
2008
- // binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
2009
- const callFn = valueN.childForFieldName('function');
2010
- if (callFn?.type === 'member_expression') {
2011
- const bindProp = callFn.childForFieldName('property');
2012
- if (bindProp?.text === 'bind') {
2013
- const boundFn = callFn.childForFieldName('object');
2014
- if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
2015
- fnRefBindings.push({ lhs: nameN.text, rhs: boundFn.text });
2016
- }
2017
- }
2018
- }
2019
- }
2356
+ collectFnRefBindings(nameN.text, valueN, fnRefBindings);
2020
2357
  }
2021
2358
 
2022
- // Constructor on the same declaration wins over annotation: the runtime type is
2023
- // what matters for call resolution (e.g. `const x: Base = new Derived()` should
2024
- // resolve `x.render()` to `Derived.render`, not `Base.render`).
2025
- // When no constructor is present, annotation still takes precedence over factory.
2359
+ // 2. Constructor wins over annotation: `const x: Base = new Derived()` resolves to Derived.
2026
2360
  if (valueN?.type === 'new_expression') {
2027
2361
  const ctorType = extractNewExprTypeName(valueN);
2028
2362
  if (ctorType) {
@@ -2031,7 +2365,7 @@ function handleVarDeclaratorTypeMap(
2031
2365
  }
2032
2366
  }
2033
2367
 
2034
- // Type annotation: const x: Foo = … → confidence 0.9
2368
+ // 3. Type annotation confidence 0.9.
2035
2369
  if (typeAnno) {
2036
2370
  const typeName = extractSimpleTypeName(typeAnno);
2037
2371
  if (typeName) {
@@ -2043,108 +2377,15 @@ function handleVarDeclaratorTypeMap(
2043
2377
  if (!valueN) return;
2044
2378
  if (valueN.type === 'new_expression') return;
2045
2379
 
2380
+ // 4a. call_expression — Object.create / return-type propagation / factory heuristic.
2046
2381
  if (valueN.type === 'call_expression') {
2047
- // Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
2048
- const createFn = valueN.childForFieldName('function');
2049
- if (createFn?.type === 'member_expression') {
2050
- const createObj = createFn.childForFieldName('object');
2051
- const createProp = createFn.childForFieldName('property');
2052
- if (createObj?.text === 'Object' && createProp?.text === 'create') {
2053
- const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
2054
- if (createArgs) {
2055
- let proto: TreeSitterNode | null = null;
2056
- for (let i = 0; i < createArgs.childCount; i++) {
2057
- const n = createArgs.child(i);
2058
- if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
2059
- proto = n;
2060
- break;
2061
- }
2062
- }
2063
- if (proto?.type === 'object') {
2064
- seedProtoProperties(nameN.text, proto, typeMap);
2065
- }
2066
- }
2067
- return;
2068
- }
2069
- }
2070
- // Phase 8.2: inter-procedural propagation — try to resolve return type from
2071
- // the local returnTypeMap before falling back to factory heuristics.
2072
- if (returnTypeMap) {
2073
- const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
2074
- if (result) {
2075
- setTypeMapEntry(typeMap, nameN.text, result.type, result.confidence);
2076
- return;
2077
- }
2078
- }
2079
- // Record for cross-file resolution in build-edges.ts (imported functions)
2080
- if (callAssignments) {
2081
- recordCallAssignment(valueN, nameN.text, typeMap, callAssignments);
2082
- }
2083
- // Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
2084
- const fn = valueN.childForFieldName('function');
2085
- if (fn?.type === 'member_expression') {
2086
- const obj = fn.childForFieldName('object');
2087
- if (obj?.type === 'identifier') {
2088
- const objName = obj.text;
2089
- if (
2090
- objName[0] &&
2091
- objName[0] !== objName[0].toLowerCase() &&
2092
- !BUILTIN_GLOBALS.has(objName)
2093
- ) {
2094
- setTypeMapEntry(typeMap, nameN.text, objName, 0.7);
2095
- }
2096
- }
2097
- }
2382
+ handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
2383
+ return;
2098
2384
  }
2099
2385
 
2100
- // Phase 8.3f: seed composite pts keys for object literal properties.
2101
- // `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
2102
- // `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
2103
- // `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
2104
- //
2105
- // For function/arrow values, the value is the qualified name ('obj.baz') because
2106
- // extractObjectLiteralFunctions now registers definitions under that qualified name to avoid
2107
- // polluting the global index with bare property names like 'init', 'run', or 'render'.
2108
- // Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
2109
- // resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
2110
- //
2111
- // Scope guard: mirrors Rust handle_var_decl's find_parent_of_types check — skip object literals
2112
- // inside function bodies so function-scoped `const localObj = { fn: ... }` never seeds
2113
- // the typeMap (which would shadow a module-level `const obj` with the same property names).
2386
+ // 4b. Object literal — seed composite pts keys for module-level const objects.
2114
2387
  if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
2115
- for (let i = 0; i < valueN.childCount; i++) {
2116
- const child = valueN.child(i);
2117
- if (!child) continue;
2118
- if (child.type === 'shorthand_property_identifier') {
2119
- setTypeMapEntry(typeMap, `${nameN.text}.${child.text}`, child.text, 0.85);
2120
- } else if (child.type === 'pair') {
2121
- const keyNode = child.childForFieldName('key');
2122
- const valNode = child.childForFieldName('value');
2123
- if (!keyNode || !valNode) continue;
2124
- const keyName =
2125
- keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
2126
- if (!keyName) continue;
2127
- const qualifiedKey = `${nameN.text}.${keyName}`;
2128
- if (
2129
- valNode.type === 'arrow_function' ||
2130
- valNode.type === 'function_expression' ||
2131
- valNode.type === 'function'
2132
- ) {
2133
- // Store the qualified name so the resolver finds the qualified definition.
2134
- setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
2135
- } else if (valNode.type === 'identifier') {
2136
- setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
2137
- }
2138
- } else if (child.type === 'method_definition') {
2139
- // Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
2140
- // extractObjectLiteralFunctions registers a definition under the qualified name;
2141
- // seed the matching typeMap entry so the two-step accessor dispatch finds it.
2142
- const nameNode = child.childForFieldName('name');
2143
- if (!nameNode) continue;
2144
- const qualifiedKey = `${nameN.text}.${nameNode.text}`;
2145
- setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
2146
- }
2147
- }
2388
+ handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
2148
2389
  }
2149
2390
  }
2150
2391
 
@@ -2737,6 +2978,28 @@ function extractReceiverName(objNode: TreeSitterNode | null): string | undefined
2737
2978
  function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
2738
2979
  const fnType = fn.type;
2739
2980
  if (fnType === 'identifier') {
2981
+ if (fn.text === 'eval') {
2982
+ // eval(code) — dynamic code execution; capture first arg if it's a string literal
2983
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
2984
+ let keyExpr: string | undefined;
2985
+ if (args) {
2986
+ for (let i = 0; i < args.childCount; i++) {
2987
+ const child = args.child(i);
2988
+ if (!child) continue;
2989
+ const t = child.type;
2990
+ if (t === '(' || t === ')' || t === ',') continue;
2991
+ if (t === 'string' || t === 'template_string') keyExpr = child.text;
2992
+ break;
2993
+ }
2994
+ }
2995
+ return {
2996
+ name: '<dynamic:eval>',
2997
+ line: nodeStartLine(callNode),
2998
+ dynamic: true,
2999
+ dynamicKind: 'eval',
3000
+ keyExpr,
3001
+ };
3002
+ }
2740
3003
  return { name: fn.text, line: nodeStartLine(callNode) };
2741
3004
  }
2742
3005
  if (fnType === 'member_expression') {
@@ -2748,6 +3011,45 @@ function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | n
2748
3011
  return null;
2749
3012
  }
2750
3013
 
3014
+ /** Return the first non-punctuation argument node from a call_expression. */
3015
+ function getFirstCallArg(callNode: TreeSitterNode): TreeSitterNode | null {
3016
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
3017
+ if (!args) return null;
3018
+ for (let i = 0; i < args.childCount; i++) {
3019
+ const child = args.child(i);
3020
+ if (!child) continue;
3021
+ const t = child.type;
3022
+ if (t === '(' || t === ')' || t === ',') continue;
3023
+ return child;
3024
+ }
3025
+ return null;
3026
+ }
3027
+
3028
+ /** Extract the logical callee from a Reflect.apply/call/construct first-arg. */
3029
+ function extractReflectCalleeFromArg(firstArg: TreeSitterNode | null, callLine: number): Call {
3030
+ if (firstArg?.type === 'identifier') {
3031
+ return { name: firstArg.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
3032
+ }
3033
+ if (firstArg?.type === 'member_expression') {
3034
+ const innerProp = firstArg.childForFieldName('property');
3035
+ if (innerProp?.type === 'identifier') {
3036
+ return {
3037
+ name: innerProp.text,
3038
+ line: callLine,
3039
+ dynamic: true,
3040
+ dynamicKind: 'reflection',
3041
+ receiver: extractReceiverName(firstArg.childForFieldName('object')),
3042
+ };
3043
+ }
3044
+ }
3045
+ return {
3046
+ name: '<dynamic:unresolved>',
3047
+ line: callLine,
3048
+ dynamic: true,
3049
+ dynamicKind: 'unresolved-dynamic',
3050
+ };
3051
+ }
3052
+
2751
3053
  /** Extract call info from a member_expression function node (obj.method()). */
2752
3054
  function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
2753
3055
  const obj = fn.childForFieldName('object');
@@ -2756,23 +3058,101 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
2756
3058
 
2757
3059
  const callLine = nodeStartLine(callNode);
2758
3060
  const propText = prop.text;
3061
+ const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
2759
3062
 
2760
- // .call()/.apply()/.bind()dynamic invocation
3063
+ // Reflect.apply(fn, thisArg, args) — extract the first arg as callee
3064
+ // Note: Reflect.call does not exist in the ECMAScript spec (only Reflect.apply, construct, get, etc.)
3065
+ if (isReflect && propText === 'apply') {
3066
+ return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
3067
+ }
3068
+
3069
+ // Reflect.construct(Target, args) — extract the constructor as the callee
3070
+ if (isReflect && propText === 'construct') {
3071
+ return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
3072
+ }
3073
+
3074
+ // Reflect.get(target, prop) — property access via reflection
3075
+ if (isReflect && propText === 'get') {
3076
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
3077
+ if (args) {
3078
+ let argIdx = 0;
3079
+ let firstArg: TreeSitterNode | null = null;
3080
+ let secondArg: TreeSitterNode | null = null;
3081
+ for (let i = 0; i < args.childCount; i++) {
3082
+ const child = args.child(i);
3083
+ if (!child) continue;
3084
+ const t = child.type;
3085
+ if (t === '(' || t === ')' || t === ',') continue;
3086
+ if (argIdx === 0) firstArg = child;
3087
+ else if (argIdx === 1) secondArg = child;
3088
+ argIdx++;
3089
+ }
3090
+ if (secondArg) {
3091
+ const receiver = firstArg ? extractReceiverName(firstArg) : undefined;
3092
+ const st = secondArg.type;
3093
+ if (st === 'string' || st === 'string_fragment') {
3094
+ const propName = secondArg.text.replace(/['"]/g, '');
3095
+ if (propName) {
3096
+ return {
3097
+ name: propName,
3098
+ line: callLine,
3099
+ dynamic: true,
3100
+ dynamicKind: 'computed-literal',
3101
+ keyExpr: secondArg.text,
3102
+ receiver,
3103
+ };
3104
+ }
3105
+ }
3106
+ if (st === 'identifier') {
3107
+ return {
3108
+ name: '<dynamic:computed-key>',
3109
+ line: callLine,
3110
+ dynamic: true,
3111
+ dynamicKind: 'computed-key',
3112
+ keyExpr: secondArg.text,
3113
+ receiver,
3114
+ };
3115
+ }
3116
+ }
3117
+ }
3118
+ return {
3119
+ name: '<dynamic:unresolved>',
3120
+ line: callLine,
3121
+ dynamic: true,
3122
+ dynamicKind: 'unresolved-dynamic',
3123
+ };
3124
+ }
3125
+
3126
+ // .call()/.apply()/.bind() — this-rebinding; the wrapped function is the real callee.
3127
+ // When the object is a plain identifier (e.g. `f.call({})`), the target is statically
3128
+ // known so we emit a static call (no dynamic flag). This keeps parity with the native
3129
+ // Rust engine, which also resolves these as dyn=0, and prevents the dynZeroEdgeRows
3130
+ // upgrade path in emitDirectCallEdgesForCall from wrongly converting a dyn=0 edge
3131
+ // (emitted by a prior direct `f()` call) to dyn=1.
3132
+ // When the object is a member_expression (e.g. `obj.method.call({})`), we still mark
3133
+ // it dynamic/reflection because the inner callee requires a second resolution hop.
2761
3134
  if (propText === 'call' || propText === 'apply' || propText === 'bind') {
2762
- if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine, dynamic: true };
3135
+ if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine };
2763
3136
  if (obj && obj.type === 'member_expression') {
2764
3137
  const innerProp = obj.childForFieldName('property');
2765
- if (innerProp) return { name: innerProp.text, line: callLine, dynamic: true };
3138
+ if (innerProp)
3139
+ return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
2766
3140
  }
2767
3141
  }
2768
3142
 
2769
- // Computed property: obj["method"]()
3143
+ // Computed string property: obj["method"]() — target is a literal; resolvable
2770
3144
  const propType = prop.type;
2771
3145
  if (propType === 'string' || propType === 'string_fragment') {
2772
3146
  const methodName = propText.replace(/['"]/g, '');
2773
3147
  if (methodName) {
2774
3148
  const receiver = extractReceiverName(obj);
2775
- return { name: methodName, line: callLine, dynamic: true, receiver };
3149
+ return {
3150
+ name: methodName,
3151
+ line: callLine,
3152
+ dynamic: true,
3153
+ dynamicKind: 'computed-literal',
3154
+ receiver,
3155
+ };
2776
3156
  }
2777
3157
  }
2778
3158
 
@@ -2780,7 +3160,7 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
2780
3160
  return { name: propText, line: callLine, receiver };
2781
3161
  }
2782
3162
 
2783
- /** Extract call info from a subscript_expression function node (obj["method"]()). */
3163
+ /** Extract call info from a subscript_expression function node (obj[key]()). */
2784
3164
  function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
2785
3165
  const obj = fn.childForFieldName('object');
2786
3166
  const index = fn.childForFieldName('index');
@@ -2795,11 +3175,32 @@ function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode):
2795
3175
  name: methodName,
2796
3176
  line: nodeStartLine(callNode),
2797
3177
  dynamic: true,
3178
+ dynamicKind: 'computed-literal',
2798
3179
  receiver,
2799
3180
  };
2800
3181
  }
2801
3182
  }
2802
- return null;
3183
+
3184
+ // obj[variable]() — key is a variable; may be resolvable via pts (RES-1), else flagged
3185
+ if (indexType === 'identifier') {
3186
+ const receiver = extractReceiverName(obj);
3187
+ return {
3188
+ name: '<dynamic:computed-key>',
3189
+ line: nodeStartLine(callNode),
3190
+ dynamic: true,
3191
+ dynamicKind: 'computed-key',
3192
+ keyExpr: index.text,
3193
+ receiver,
3194
+ };
3195
+ }
3196
+
3197
+ // Any other index expression (binary, call, template with ${}…) — not statically resolvable
3198
+ return {
3199
+ name: '<dynamic:unresolved>',
3200
+ line: nodeStartLine(callNode),
3201
+ dynamic: true,
3202
+ dynamicKind: 'unresolved-dynamic',
3203
+ };
2803
3204
  }
2804
3205
 
2805
3206
  /**
@@ -3118,6 +3519,10 @@ function runCollectorWalk(rootNode: TreeSitterNode, targets: CollectorWalkTarget
3118
3519
  if (name) targets.newExpressions.push(name);
3119
3520
  break;
3120
3521
  }
3522
+ case 'decorator': {
3523
+ if (targets.calls) handleDecorator(node, targets.calls);
3524
+ break;
3525
+ }
3121
3526
  case 'field_definition':
3122
3527
  case 'public_field_definition':
3123
3528
  if (targets.classMemberDefs) handleFieldDef(node, targets.classMemberDefs);