@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
@@ -64,6 +64,13 @@ const BUILTIN_GLOBALS = new Set([
64
64
  const MAX_PROPAGATION_DEPTH = 3;
65
65
  /** Confidence penalty applied per propagation hop (1.0 → 0.9 → 0.8 → 0.7). */
66
66
  export const PROPAGATION_HOP_PENALTY = 0.1;
67
+ /**
68
+ * Confidence score for a return type inferred from `return new Constructor()` with no
69
+ * explicit TypeScript annotation. Registered as `analysis.typeInferenceConfidence` in
70
+ * `src/infrastructure/config.ts` DEFAULTS — kept in sync manually until config is
71
+ * threaded through to `extractSymbols`.
72
+ */
73
+ const INFERRED_RETURN_TYPE_CONFIDENCE = 0.85;
67
74
  /**
68
75
  * Extract symbols from a JS/TS parsed AST.
69
76
  * When a compiled tree-sitter Query is provided (from parser.js),
@@ -252,13 +259,16 @@ function dispatchQueryMatch(c, definitions, calls, imports, classes, exps) {
252
259
  handleExportCapture(c, exps, imports);
253
260
  }
254
261
  else if (c.callfn_node) {
255
- calls.push({
256
- name: c.callfn_name.text,
257
- line: nodeStartLine(c.callfn_node),
258
- });
262
+ // Route through extractCallInfo so special identifier calls (eval) get classified.
263
+ const callfnInfo = extractCallInfo(c.callfn_name, c.callfn_node);
264
+ if (callfnInfo)
265
+ calls.push(callfnInfo);
259
266
  calls.push(...extractCallbackReferenceCalls(c.callfn_node));
260
267
  }
261
268
  else if (c.callmem_node) {
269
+ // extractCallInfo → extractMemberExprCallInfo applies the plain-identifier guard for
270
+ // .call/.apply/.bind: when the object is a bare identifier (e.g. `fn.call(ctx)`),
271
+ // the call is emitted as static (no dynamic flag), matching the walk path and native engine.
262
272
  const callInfo = extractCallInfo(c.callmem_fn, c.callmem_node);
263
273
  if (callInfo)
264
274
  calls.push(callInfo);
@@ -274,10 +284,21 @@ function dispatchQueryMatch(c, definitions, calls, imports, classes, exps) {
274
284
  calls.push(...extractCallbackReferenceCalls(c.callsub_node));
275
285
  }
276
286
  else if (c.newfn_node) {
277
- calls.push({
278
- name: c.newfn_name.text,
279
- line: nodeStartLine(c.newfn_node),
280
- });
287
+ if (c.newfn_name.text === 'Function') {
288
+ // new Function(body) — dynamic code execution; classify as eval kind
289
+ calls.push({
290
+ name: '<dynamic:eval>',
291
+ line: nodeStartLine(c.newfn_node),
292
+ dynamic: true,
293
+ dynamicKind: 'eval',
294
+ });
295
+ }
296
+ else {
297
+ calls.push({
298
+ name: c.newfn_name.text,
299
+ line: nodeStartLine(c.newfn_node),
300
+ });
301
+ }
281
302
  }
282
303
  else if (c.newmem_node) {
283
304
  const callInfo = extractCallInfo(c.newmem_fn, c.newmem_node);
@@ -333,8 +354,11 @@ function extractSymbolsQuery(tree, query) {
333
354
  forOfBindings,
334
355
  arrayCallbackBindings,
335
356
  });
336
- // Extract definitions from destructured bindings (query patterns don't match object_pattern)
337
- extractDestructuredBindingsWalk(tree.rootNode, definitions);
357
+ // Extract definitions from destructured bindings (query patterns don't match object_pattern).
358
+ // Also collects CJS require bindings (const { X } = require('…')) into a separate list so
359
+ // importedNames can classify them as import artifacts without creating DB edges (#1661).
360
+ const cjsRequireBindings = [];
361
+ extractDestructuredBindingsWalk(tree.rootNode, definitions, cjsRequireBindings);
338
362
  // Everything without bespoke traversal semantics is collected in ONE pass:
339
363
  // dynamic import() calls, prototype-method definitions, param bindings,
340
364
  // array-element bindings, object-prop bindings, `new X()` names,
@@ -375,6 +399,7 @@ function extractSymbolsQuery(tree, query) {
375
399
  thisCallBindings,
376
400
  newExpressions,
377
401
  ...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
402
+ ...(cjsRequireBindings.length > 0 ? { cjsRequireBindings } : {}),
378
403
  };
379
404
  }
380
405
  /** Node types that define a function scope — constants inside these are skipped. */
@@ -421,6 +446,7 @@ function extractConstantsWalk(node, definitions) {
421
446
  declNode = inner;
422
447
  }
423
448
  extractConstDeclarators(declNode, definitions);
449
+ extractLetVarObjLiteralDeclarators(declNode, definitions);
424
450
  // Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
425
451
  if (child.type !== 'export_statement') {
426
452
  extractConstantsWalk(child, definitions);
@@ -434,8 +460,11 @@ function extractConstantsWalk(node, definitions) {
434
460
  /**
435
461
  * Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
436
462
  * e.g. `const { handleToken, checkPermissions } = initAuth(config)`
463
+ *
464
+ * When `cjsRequireBindings` is provided, also records `const { X } = require('./path')` patterns
465
+ * so the edge builder can classify X as an import artifact rather than a local definition (#1661).
437
466
  */
438
- function extractDestructuredBindingsWalk(node, definitions) {
467
+ function extractDestructuredBindingsWalk(node, definitions, cjsRequireBindings) {
439
468
  for (let i = 0; i < node.childCount; i++) {
440
469
  const child = node.child(i);
441
470
  if (!child)
@@ -458,6 +487,41 @@ function extractDestructuredBindingsWalk(node, definitions) {
458
487
  const nameN = declarator.childForFieldName('name');
459
488
  if (nameN && nameN.type === 'object_pattern') {
460
489
  extractDestructuredBindings(nameN, nodeStartLine(declNode), nodeEndLine(declNode), definitions);
490
+ // Record CJS require bindings so importedNames can classify these names
491
+ // as import artifacts, preventing false local-definition blocking (#1661).
492
+ if (cjsRequireBindings) {
493
+ const valueN = declarator.childForFieldName('value');
494
+ if (valueN?.type === 'call_expression') {
495
+ const fn = valueN.childForFieldName('function');
496
+ if (fn?.text === 'require') {
497
+ const args = valueN.childForFieldName('arguments');
498
+ const strArg = args && findChild(args, 'string');
499
+ if (strArg) {
500
+ const modPath = strArg.text.replace(/['"]/g, '');
501
+ const names = [];
502
+ for (let k = 0; k < nameN.childCount; k++) {
503
+ const prop = nameN.child(k);
504
+ if (!prop)
505
+ continue;
506
+ if (prop.type === 'shorthand_property_identifier_pattern' ||
507
+ prop.type === 'shorthand_property_identifier') {
508
+ names.push(prop.text);
509
+ }
510
+ else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
511
+ const val = prop.childForFieldName('value');
512
+ if (val?.type === 'identifier' ||
513
+ val?.type === 'shorthand_property_identifier_pattern') {
514
+ names.push(val.text);
515
+ }
516
+ }
517
+ }
518
+ if (names.length > 0) {
519
+ cjsRequireBindings.push({ names, source: modPath });
520
+ }
521
+ }
522
+ }
523
+ }
524
+ }
461
525
  }
462
526
  else if (nameN && nameN.type === 'array_pattern') {
463
527
  // `const [x, y] = ...` — emit a single constant node whose name is the
@@ -472,7 +536,7 @@ function extractDestructuredBindingsWalk(node, definitions) {
472
536
  }
473
537
  }
474
538
  if (child.type !== 'export_statement') {
475
- extractDestructuredBindingsWalk(child, definitions);
539
+ extractDestructuredBindingsWalk(child, definitions, cjsRequireBindings);
476
540
  }
477
541
  }
478
542
  }
@@ -516,6 +580,33 @@ function extractConstDeclarators(declNode, definitions) {
516
580
  }
517
581
  }
518
582
  }
583
+ /**
584
+ * Extract qualified method definitions from `let`/`var` object-literal declarations.
585
+ * Mirrors `match_js_objlit_qualified_method_defs` in `javascript.rs`, which emits
586
+ * qualified definitions for `method_definition` (all declaration kinds) and
587
+ * `pair+arrow/function` (`let`/`var` only, since `const` is already handled by
588
+ * `extractConstDeclarators` → `extractObjectLiteralFunctions`).
589
+ *
590
+ * Called from extractConstantsWalk which already provides the function-scope guard.
591
+ * `var q1 = { m1() {} }` → emits Definition { name: 'q1.m1', kind: 'function' }
592
+ */
593
+ function extractLetVarObjLiteralDeclarators(declNode, definitions) {
594
+ const t = declNode.type;
595
+ if (t !== 'lexical_declaration' && t !== 'variable_declaration')
596
+ return;
597
+ if (declNode.text.startsWith('const '))
598
+ return; // handled by extractConstDeclarators
599
+ for (let j = 0; j < declNode.childCount; j++) {
600
+ const declarator = declNode.child(j);
601
+ if (declarator?.type !== 'variable_declarator')
602
+ continue;
603
+ const nameN = declarator.childForFieldName('name');
604
+ const valueN = declarator.childForFieldName('value');
605
+ if (nameN?.type !== 'identifier' || !valueN || valueN.type !== 'object')
606
+ continue;
607
+ extractObjectLiteralFunctions(valueN, nameN.text, definitions);
608
+ }
609
+ }
519
610
  /**
520
611
  * Recursive walk to find dynamic import() calls.
521
612
  * Query patterns match call_expression with identifier/member_expression/subscript_expression
@@ -692,6 +783,9 @@ function walkJavaScriptNode(node, ctx) {
692
783
  case 'enum_declaration':
693
784
  handleEnumDecl(node, ctx);
694
785
  break;
786
+ case 'decorator':
787
+ handleDecorator(node, ctx.calls);
788
+ break;
695
789
  case 'call_expression':
696
790
  handleCallExpr(node, ctx);
697
791
  break;
@@ -945,6 +1039,17 @@ function handleVariableDecl(node, ctx) {
945
1039
  extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
946
1040
  }
947
1041
  }
1042
+ else if (!isConst &&
1043
+ nameN.type === 'identifier' &&
1044
+ valueN.type === 'object' &&
1045
+ !hasFunctionScopeAncestor(node)) {
1046
+ // `let`/`var` object literals: extract qualified method definitions so that
1047
+ // `obj.method()` calls resolve correctly. Mirrors Rust match_js_objlit_qualified_method_defs
1048
+ // which emits method_definition qualified names for ALL declaration kinds and
1049
+ // pair+arrow/function for let/var only (const is already handled above).
1050
+ // Scope guard prevents local object properties from polluting the global index.
1051
+ extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
1052
+ }
948
1053
  else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
949
1054
  // Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
950
1055
  // Each destructured property becomes a function definition so it can be
@@ -954,6 +1059,39 @@ function handleVariableDecl(node, ctx) {
954
1059
  // Scope guard mirrors extractDestructuredBindingsWalk (query path) and
955
1060
  // handle_var_decl (Rust path) — skips bindings inside function bodies.
956
1061
  extractDestructuredBindings(nameN, nodeStartLine(node), nodeEndLine(node), ctx.definitions);
1062
+ // Record CJS require bindings for import-artifact classification (#1661).
1063
+ if (valueN?.type === 'call_expression') {
1064
+ const fn = valueN.childForFieldName('function');
1065
+ if (fn?.text === 'require') {
1066
+ const args = valueN.childForFieldName('arguments');
1067
+ const strArg = args && findChild(args, 'string');
1068
+ if (strArg) {
1069
+ const modPath = strArg.text.replace(/['"]/g, '');
1070
+ const names = [];
1071
+ for (let k = 0; k < nameN.childCount; k++) {
1072
+ const prop = nameN.child(k);
1073
+ if (!prop)
1074
+ continue;
1075
+ if (prop.type === 'shorthand_property_identifier_pattern' ||
1076
+ prop.type === 'shorthand_property_identifier') {
1077
+ names.push(prop.text);
1078
+ }
1079
+ else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
1080
+ const val = prop.childForFieldName('value');
1081
+ if (val?.type === 'identifier' ||
1082
+ val?.type === 'shorthand_property_identifier_pattern') {
1083
+ names.push(val.text);
1084
+ }
1085
+ }
1086
+ }
1087
+ if (names.length > 0) {
1088
+ if (!ctx.cjsRequireBindings)
1089
+ ctx.cjsRequireBindings = [];
1090
+ ctx.cjsRequireBindings.push({ names, source: modPath });
1091
+ }
1092
+ }
1093
+ }
1094
+ }
957
1095
  }
958
1096
  else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
959
1097
  // Array destructuring: `const [x, y] = ...` — emit a single constant node
@@ -1119,7 +1257,18 @@ function handleNewExpr(node, ctx) {
1119
1257
  if (!ctor)
1120
1258
  return;
1121
1259
  if (ctor.type === 'identifier') {
1122
- ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
1260
+ if (ctor.text === 'Function') {
1261
+ // new Function(body) — dynamic code execution; undecidable static target
1262
+ ctx.calls.push({
1263
+ name: '<dynamic:eval>',
1264
+ line: nodeStartLine(node),
1265
+ dynamic: true,
1266
+ dynamicKind: 'eval',
1267
+ });
1268
+ }
1269
+ else {
1270
+ ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
1271
+ }
1123
1272
  }
1124
1273
  else if (ctor.type === 'member_expression') {
1125
1274
  const callInfo = extractCallInfo(ctor, node);
@@ -1127,6 +1276,39 @@ function handleNewExpr(node, ctx) {
1127
1276
  ctx.calls.push(callInfo);
1128
1277
  }
1129
1278
  }
1279
+ /**
1280
+ * Handle a TypeScript/JS decorator node.
1281
+ *
1282
+ * Only handles bare-identifier and bare-member-expression decorators
1283
+ * (`@Foo`, `@Foo.bar`) since decorated call expressions (`@Foo()`, `@Foo.bar()`)
1284
+ * are already visited as `call_expression` children by the recursive walker.
1285
+ */
1286
+ function handleDecorator(node, calls) {
1287
+ // Decorators wrap their expression; find the first non-@ child
1288
+ for (let i = 0; i < node.childCount; i++) {
1289
+ const child = node.child(i);
1290
+ if (!child || child.type === '@')
1291
+ continue;
1292
+ const t = child.type;
1293
+ if (t === 'identifier') {
1294
+ // @Foo — the identifier is the decorator factory; emit as reflection call
1295
+ calls.push({
1296
+ name: child.text,
1297
+ line: nodeStartLine(node),
1298
+ dynamic: true,
1299
+ dynamicKind: 'reflection',
1300
+ });
1301
+ }
1302
+ else if (t === 'member_expression') {
1303
+ // @Foo.bar — emit as reflection; always mark dynamic since it's decorator dispatch
1304
+ const callInfo = extractCallInfo(child, node);
1305
+ if (callInfo)
1306
+ calls.push({ ...callInfo, dynamic: true, dynamicKind: 'reflection' });
1307
+ }
1308
+ // call_expression / other — handled by the recursive walker automatically
1309
+ break;
1310
+ }
1311
+ }
1130
1312
  /** Handle a dynamic import() call expression and add to imports if static. */
1131
1313
  function handleDynamicImportCall(node, imports) {
1132
1314
  const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
@@ -1482,8 +1664,8 @@ function storeReturnType(fnNode, fnName, returnTypeMap) {
1482
1664
  const inferred = findReturnNewExprType(body);
1483
1665
  if (inferred) {
1484
1666
  const existing = returnTypeMap.get(fnName);
1485
- if (!existing || 0.85 > existing.confidence)
1486
- returnTypeMap.set(fnName, { type: inferred, confidence: 0.85 });
1667
+ if (!existing || INFERRED_RETURN_TYPE_CONFIDENCE > existing.confidence)
1668
+ returnTypeMap.set(fnName, { type: inferred, confidence: INFERRED_RETURN_TYPE_CONFIDENCE });
1487
1669
  }
1488
1670
  }
1489
1671
  }
@@ -1824,54 +2006,186 @@ function runContextCollectorWalk(rootNode, out) {
1824
2006
  };
1825
2007
  walk(rootNode, 0, null, null);
1826
2008
  }
1827
- /** Extract type info from a variable_declarator: type annotation, constructor, or factory. */
1828
- function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignments, fnRefBindings) {
1829
- const nameN = node.childForFieldName('name');
1830
- if (nameN?.type !== 'identifier')
2009
+ /**
2010
+ * Record function-reference bindings from a variable_declarator's value node.
2011
+ *
2012
+ * Captures three patterns (Phase 8.3):
2013
+ * - `const fn = handler` (identifier alias)
2014
+ * - `const fn = obj.method` (member_expression alias)
2015
+ * - `const f = fn.bind(ctx)` (bind creates a bound alias)
2016
+ *
2017
+ * Must be called before any type-analysis early returns so every declarator
2018
+ * contributes to fnRefBindings regardless of whether it has a type annotation.
2019
+ */
2020
+ function collectFnRefBindings(lhsName, valueN, fnRefBindings) {
2021
+ if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
2022
+ fnRefBindings.push({ lhs: lhsName, rhs: valueN.text });
1831
2023
  return;
1832
- const typeAnno = findChild(node, 'type_annotation');
1833
- const valueN = node.childForFieldName('value');
1834
- // Phase 8.3: record function-reference bindings before any type-analysis early returns.
1835
- // Captures `const fn = handler` (identifier) and `const fn = obj.method` (member_expression).
1836
- // Also handles `const f = fn.bind(ctx)` bind returns a new function aliasing fn.
1837
- if (fnRefBindings && valueN) {
1838
- if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
1839
- fnRefBindings.push({ lhs: nameN.text, rhs: valueN.text });
1840
- }
1841
- else if (valueN.type === 'member_expression') {
1842
- const prop = valueN.childForFieldName('property');
1843
- const obj = valueN.childForFieldName('object');
1844
- // Guard: only static property access (property_identifier or identifier), not
1845
- // computed subscript expressions like obj[expr] where prop.text would be the
1846
- // full expression rather than a simple name — those can never match pts keys.
1847
- if (prop &&
1848
- (prop.type === 'property_identifier' || prop.type === 'identifier') &&
1849
- obj?.type === 'identifier' &&
1850
- !BUILTIN_GLOBALS.has(obj.text)) {
1851
- fnRefBindings.push({ lhs: nameN.text, rhs: prop.text, rhsReceiver: obj.text });
2024
+ }
2025
+ if (valueN.type === 'member_expression') {
2026
+ const prop = valueN.childForFieldName('property');
2027
+ const obj = valueN.childForFieldName('object');
2028
+ // Guard: only static property access (property_identifier or identifier), not
2029
+ // computed subscript expressions like obj[expr] where prop.text would be the
2030
+ // full expression rather than a simple name — those can never match pts keys.
2031
+ if (prop &&
2032
+ (prop.type === 'property_identifier' || prop.type === 'identifier') &&
2033
+ obj?.type === 'identifier' &&
2034
+ !BUILTIN_GLOBALS.has(obj.text)) {
2035
+ fnRefBindings.push({ lhs: lhsName, rhs: prop.text, rhsReceiver: obj.text });
2036
+ }
2037
+ return;
2038
+ }
2039
+ if (valueN.type === 'call_expression') {
2040
+ // `const f = fn.bind(ctx)` bind returns a bound copy of fn; track f → fn so
2041
+ // pts(f) pts(fn) and subsequent `f(args)` calls resolve to fn.
2042
+ // Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
2043
+ // binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
2044
+ const callFn = valueN.childForFieldName('function');
2045
+ if (callFn?.type === 'member_expression') {
2046
+ const bindProp = callFn.childForFieldName('property');
2047
+ if (bindProp?.text === 'bind') {
2048
+ const boundFn = callFn.childForFieldName('object');
2049
+ if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
2050
+ fnRefBindings.push({ lhs: lhsName, rhs: boundFn.text });
2051
+ }
1852
2052
  }
1853
2053
  }
1854
- else if (valueN.type === 'call_expression') {
1855
- // `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
1856
- // pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
1857
- // Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
1858
- // binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
1859
- const callFn = valueN.childForFieldName('function');
1860
- if (callFn?.type === 'member_expression') {
1861
- const bindProp = callFn.childForFieldName('property');
1862
- if (bindProp?.text === 'bind') {
1863
- const boundFn = callFn.childForFieldName('object');
1864
- if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
1865
- fnRefBindings.push({ lhs: nameN.text, rhs: boundFn.text });
2054
+ }
2055
+ }
2056
+ /**
2057
+ * Handle the `call_expression` branch of variable_declarator type-map seeding.
2058
+ *
2059
+ * Processes three sub-cases in priority order:
2060
+ * 1. Object.create({ ... }) — seeds composite pts keys from the prototype object (Phase 8.3e)
2061
+ * 2. Inter-procedural return-type propagation via returnTypeMap (Phase 8.2)
2062
+ * 3. Factory method heuristic: `const x = Foo.create()` → type Foo at confidence 0.7
2063
+ */
2064
+ function handleCallExprTypeMap(lhsName, valueN, typeMap, returnTypeMap, callAssignments) {
2065
+ const createFn = valueN.childForFieldName('function');
2066
+ // Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
2067
+ if (createFn?.type === 'member_expression') {
2068
+ const createObj = createFn.childForFieldName('object');
2069
+ const createProp = createFn.childForFieldName('property');
2070
+ if (createObj?.text === 'Object' && createProp?.text === 'create') {
2071
+ const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
2072
+ if (createArgs) {
2073
+ let proto = null;
2074
+ for (let i = 0; i < createArgs.childCount; i++) {
2075
+ const n = createArgs.child(i);
2076
+ if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
2077
+ proto = n;
2078
+ break;
1866
2079
  }
1867
2080
  }
2081
+ if (proto?.type === 'object') {
2082
+ seedProtoProperties(lhsName, proto, typeMap);
2083
+ }
1868
2084
  }
2085
+ return;
2086
+ }
2087
+ }
2088
+ // Phase 8.2: inter-procedural propagation — try to resolve return type from
2089
+ // the local returnTypeMap before falling back to factory heuristics.
2090
+ if (returnTypeMap) {
2091
+ const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
2092
+ if (result) {
2093
+ setTypeMapEntry(typeMap, lhsName, result.type, result.confidence);
2094
+ return;
2095
+ }
2096
+ }
2097
+ // Record for cross-file resolution in build-edges.ts (imported functions)
2098
+ if (callAssignments) {
2099
+ recordCallAssignment(valueN, lhsName, typeMap, callAssignments);
2100
+ }
2101
+ // Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
2102
+ if (createFn?.type === 'member_expression') {
2103
+ const obj = createFn.childForFieldName('object');
2104
+ if (obj?.type === 'identifier') {
2105
+ const objName = obj.text;
2106
+ if (objName[0] && objName[0] !== objName[0].toLowerCase() && !BUILTIN_GLOBALS.has(objName)) {
2107
+ setTypeMapEntry(typeMap, lhsName, objName, 0.7);
2108
+ }
2109
+ }
2110
+ }
2111
+ }
2112
+ /**
2113
+ * Seed composite pts keys from a module-level object literal assignment (Phase 8.3f).
2114
+ *
2115
+ * `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
2116
+ * `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
2117
+ * `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
2118
+ * `const obj = { baz() {} }` (method shorthand) → typeMap['obj.baz'] = 'obj.baz'
2119
+ *
2120
+ * For function/arrow values, the value is the qualified name ('obj.baz') because
2121
+ * extractObjectLiteralFunctions registers definitions under that qualified name to avoid
2122
+ * polluting the global index with bare property names like 'init', 'run', or 'render'.
2123
+ * Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
2124
+ * resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
2125
+ *
2126
+ * Scope guard: caller must ensure `node` is not inside a function body
2127
+ * (mirrors Rust handle_var_decl's find_parent_of_types check — function-scoped
2128
+ * `const localObj = { fn: ... }` must not shadow a module-level `const obj`).
2129
+ */
2130
+ function handleObjectLiteralTypeMap(lhsName, valueN, typeMap) {
2131
+ for (let i = 0; i < valueN.childCount; i++) {
2132
+ const child = valueN.child(i);
2133
+ if (!child)
2134
+ continue;
2135
+ if (child.type === 'shorthand_property_identifier') {
2136
+ setTypeMapEntry(typeMap, `${lhsName}.${child.text}`, child.text, 0.85);
2137
+ }
2138
+ else if (child.type === 'pair') {
2139
+ const keyNode = child.childForFieldName('key');
2140
+ const valNode = child.childForFieldName('value');
2141
+ if (!keyNode || !valNode)
2142
+ continue;
2143
+ const keyName = keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
2144
+ if (!keyName)
2145
+ continue;
2146
+ const qualifiedKey = `${lhsName}.${keyName}`;
2147
+ if (valNode.type === 'arrow_function' ||
2148
+ valNode.type === 'function_expression' ||
2149
+ valNode.type === 'function') {
2150
+ // Store the qualified name so the resolver finds the qualified definition.
2151
+ setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
2152
+ }
2153
+ else if (valNode.type === 'identifier') {
2154
+ setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
2155
+ }
2156
+ }
2157
+ else if (child.type === 'method_definition') {
2158
+ // Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
2159
+ // extractObjectLiteralFunctions registers a definition under the qualified name;
2160
+ // seed the matching typeMap entry so the two-step accessor dispatch finds it.
2161
+ const nameNode = child.childForFieldName('name');
2162
+ if (!nameNode)
2163
+ continue;
2164
+ setTypeMapEntry(typeMap, `${lhsName}.${nameNode.text}`, `${lhsName}.${nameNode.text}`, 0.85);
1869
2165
  }
1870
2166
  }
1871
- // Constructor on the same declaration wins over annotation: the runtime type is
1872
- // what matters for call resolution (e.g. `const x: Base = new Derived()` should
1873
- // resolve `x.render()` to `Derived.render`, not `Base.render`).
1874
- // When no constructor is present, annotation still takes precedence over factory.
2167
+ }
2168
+ /**
2169
+ * Extract type info from a variable_declarator: type annotation, constructor, or factory.
2170
+ *
2171
+ * Orchestrates four concerns in priority order:
2172
+ * 1. fnRefBindings — always collected first (before any early return)
2173
+ * 2. new_expression — constructor wins over annotation (runtime type is authoritative)
2174
+ * 3. type_annotation — confidence 0.9 for static analysis
2175
+ * 4. call_expression / object literal — delegated to handleCallExprTypeMap /
2176
+ * handleObjectLiteralTypeMap
2177
+ */
2178
+ function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignments, fnRefBindings) {
2179
+ const nameN = node.childForFieldName('name');
2180
+ if (nameN?.type !== 'identifier')
2181
+ return;
2182
+ const typeAnno = findChild(node, 'type_annotation');
2183
+ const valueN = node.childForFieldName('value');
2184
+ // 1. fnRefBindings — must run before any early return so every declarator contributes.
2185
+ if (fnRefBindings && valueN) {
2186
+ collectFnRefBindings(nameN.text, valueN, fnRefBindings);
2187
+ }
2188
+ // 2. Constructor wins over annotation: `const x: Base = new Derived()` resolves to Derived.
1875
2189
  if (valueN?.type === 'new_expression') {
1876
2190
  const ctorType = extractNewExprTypeName(valueN);
1877
2191
  if (ctorType) {
@@ -1879,7 +2193,7 @@ function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignment
1879
2193
  return;
1880
2194
  }
1881
2195
  }
1882
- // Type annotation: const x: Foo = … → confidence 0.9
2196
+ // 3. Type annotation confidence 0.9.
1883
2197
  if (typeAnno) {
1884
2198
  const typeName = extractSimpleTypeName(typeAnno);
1885
2199
  if (typeName) {
@@ -1891,109 +2205,14 @@ function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignment
1891
2205
  return;
1892
2206
  if (valueN.type === 'new_expression')
1893
2207
  return;
2208
+ // 4a. call_expression — Object.create / return-type propagation / factory heuristic.
1894
2209
  if (valueN.type === 'call_expression') {
1895
- // Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
1896
- const createFn = valueN.childForFieldName('function');
1897
- if (createFn?.type === 'member_expression') {
1898
- const createObj = createFn.childForFieldName('object');
1899
- const createProp = createFn.childForFieldName('property');
1900
- if (createObj?.text === 'Object' && createProp?.text === 'create') {
1901
- const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
1902
- if (createArgs) {
1903
- let proto = null;
1904
- for (let i = 0; i < createArgs.childCount; i++) {
1905
- const n = createArgs.child(i);
1906
- if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
1907
- proto = n;
1908
- break;
1909
- }
1910
- }
1911
- if (proto?.type === 'object') {
1912
- seedProtoProperties(nameN.text, proto, typeMap);
1913
- }
1914
- }
1915
- return;
1916
- }
1917
- }
1918
- // Phase 8.2: inter-procedural propagation — try to resolve return type from
1919
- // the local returnTypeMap before falling back to factory heuristics.
1920
- if (returnTypeMap) {
1921
- const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
1922
- if (result) {
1923
- setTypeMapEntry(typeMap, nameN.text, result.type, result.confidence);
1924
- return;
1925
- }
1926
- }
1927
- // Record for cross-file resolution in build-edges.ts (imported functions)
1928
- if (callAssignments) {
1929
- recordCallAssignment(valueN, nameN.text, typeMap, callAssignments);
1930
- }
1931
- // Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
1932
- const fn = valueN.childForFieldName('function');
1933
- if (fn?.type === 'member_expression') {
1934
- const obj = fn.childForFieldName('object');
1935
- if (obj?.type === 'identifier') {
1936
- const objName = obj.text;
1937
- if (objName[0] &&
1938
- objName[0] !== objName[0].toLowerCase() &&
1939
- !BUILTIN_GLOBALS.has(objName)) {
1940
- setTypeMapEntry(typeMap, nameN.text, objName, 0.7);
1941
- }
1942
- }
1943
- }
2210
+ handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
2211
+ return;
1944
2212
  }
1945
- // Phase 8.3f: seed composite pts keys for object literal properties.
1946
- // `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
1947
- // `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
1948
- // `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
1949
- //
1950
- // For function/arrow values, the value is the qualified name ('obj.baz') because
1951
- // extractObjectLiteralFunctions now registers definitions under that qualified name to avoid
1952
- // polluting the global index with bare property names like 'init', 'run', or 'render'.
1953
- // Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
1954
- // resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
1955
- //
1956
- // Scope guard: mirrors Rust handle_var_decl's find_parent_of_types check — skip object literals
1957
- // inside function bodies so function-scoped `const localObj = { fn: ... }` never seeds
1958
- // the typeMap (which would shadow a module-level `const obj` with the same property names).
2213
+ // 4b. Object literal — seed composite pts keys for module-level const objects.
1959
2214
  if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
1960
- for (let i = 0; i < valueN.childCount; i++) {
1961
- const child = valueN.child(i);
1962
- if (!child)
1963
- continue;
1964
- if (child.type === 'shorthand_property_identifier') {
1965
- setTypeMapEntry(typeMap, `${nameN.text}.${child.text}`, child.text, 0.85);
1966
- }
1967
- else if (child.type === 'pair') {
1968
- const keyNode = child.childForFieldName('key');
1969
- const valNode = child.childForFieldName('value');
1970
- if (!keyNode || !valNode)
1971
- continue;
1972
- const keyName = keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
1973
- if (!keyName)
1974
- continue;
1975
- const qualifiedKey = `${nameN.text}.${keyName}`;
1976
- if (valNode.type === 'arrow_function' ||
1977
- valNode.type === 'function_expression' ||
1978
- valNode.type === 'function') {
1979
- // Store the qualified name so the resolver finds the qualified definition.
1980
- setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
1981
- }
1982
- else if (valNode.type === 'identifier') {
1983
- setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
1984
- }
1985
- }
1986
- else if (child.type === 'method_definition') {
1987
- // Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
1988
- // extractObjectLiteralFunctions registers a definition under the qualified name;
1989
- // seed the matching typeMap entry so the two-step accessor dispatch finds it.
1990
- const nameNode = child.childForFieldName('name');
1991
- if (!nameNode)
1992
- continue;
1993
- const qualifiedKey = `${nameN.text}.${nameNode.text}`;
1994
- setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
1995
- }
1996
- }
2215
+ handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
1997
2216
  }
1998
2217
  }
1999
2218
  /** Extract type info from a required_parameter or optional_parameter. */
@@ -2579,6 +2798,31 @@ function extractReceiverName(objNode) {
2579
2798
  function extractCallInfo(fn, callNode) {
2580
2799
  const fnType = fn.type;
2581
2800
  if (fnType === 'identifier') {
2801
+ if (fn.text === 'eval') {
2802
+ // eval(code) — dynamic code execution; capture first arg if it's a string literal
2803
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
2804
+ let keyExpr;
2805
+ if (args) {
2806
+ for (let i = 0; i < args.childCount; i++) {
2807
+ const child = args.child(i);
2808
+ if (!child)
2809
+ continue;
2810
+ const t = child.type;
2811
+ if (t === '(' || t === ')' || t === ',')
2812
+ continue;
2813
+ if (t === 'string' || t === 'template_string')
2814
+ keyExpr = child.text;
2815
+ break;
2816
+ }
2817
+ }
2818
+ return {
2819
+ name: '<dynamic:eval>',
2820
+ line: nodeStartLine(callNode),
2821
+ dynamic: true,
2822
+ dynamicKind: 'eval',
2823
+ keyExpr,
2824
+ };
2825
+ }
2582
2826
  return { name: fn.text, line: nodeStartLine(callNode) };
2583
2827
  }
2584
2828
  if (fnType === 'member_expression') {
@@ -2589,6 +2833,46 @@ function extractCallInfo(fn, callNode) {
2589
2833
  }
2590
2834
  return null;
2591
2835
  }
2836
+ /** Return the first non-punctuation argument node from a call_expression. */
2837
+ function getFirstCallArg(callNode) {
2838
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
2839
+ if (!args)
2840
+ return null;
2841
+ for (let i = 0; i < args.childCount; i++) {
2842
+ const child = args.child(i);
2843
+ if (!child)
2844
+ continue;
2845
+ const t = child.type;
2846
+ if (t === '(' || t === ')' || t === ',')
2847
+ continue;
2848
+ return child;
2849
+ }
2850
+ return null;
2851
+ }
2852
+ /** Extract the logical callee from a Reflect.apply/call/construct first-arg. */
2853
+ function extractReflectCalleeFromArg(firstArg, callLine) {
2854
+ if (firstArg?.type === 'identifier') {
2855
+ return { name: firstArg.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
2856
+ }
2857
+ if (firstArg?.type === 'member_expression') {
2858
+ const innerProp = firstArg.childForFieldName('property');
2859
+ if (innerProp?.type === 'identifier') {
2860
+ return {
2861
+ name: innerProp.text,
2862
+ line: callLine,
2863
+ dynamic: true,
2864
+ dynamicKind: 'reflection',
2865
+ receiver: extractReceiverName(firstArg.childForFieldName('object')),
2866
+ };
2867
+ }
2868
+ }
2869
+ return {
2870
+ name: '<dynamic:unresolved>',
2871
+ line: callLine,
2872
+ dynamic: true,
2873
+ dynamicKind: 'unresolved-dynamic',
2874
+ };
2875
+ }
2592
2876
  /** Extract call info from a member_expression function node (obj.method()). */
2593
2877
  function extractMemberExprCallInfo(fn, callNode) {
2594
2878
  const obj = fn.childForFieldName('object');
@@ -2597,29 +2881,107 @@ function extractMemberExprCallInfo(fn, callNode) {
2597
2881
  return null;
2598
2882
  const callLine = nodeStartLine(callNode);
2599
2883
  const propText = prop.text;
2600
- // .call()/.apply()/.bind() dynamic invocation
2884
+ const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
2885
+ // Reflect.apply(fn, thisArg, args) — extract the first arg as callee
2886
+ // Note: Reflect.call does not exist in the ECMAScript spec (only Reflect.apply, construct, get, etc.)
2887
+ if (isReflect && propText === 'apply') {
2888
+ return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
2889
+ }
2890
+ // Reflect.construct(Target, args) — extract the constructor as the callee
2891
+ if (isReflect && propText === 'construct') {
2892
+ return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
2893
+ }
2894
+ // Reflect.get(target, prop) — property access via reflection
2895
+ if (isReflect && propText === 'get') {
2896
+ const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
2897
+ if (args) {
2898
+ let argIdx = 0;
2899
+ let firstArg = null;
2900
+ let secondArg = null;
2901
+ for (let i = 0; i < args.childCount; i++) {
2902
+ const child = args.child(i);
2903
+ if (!child)
2904
+ continue;
2905
+ const t = child.type;
2906
+ if (t === '(' || t === ')' || t === ',')
2907
+ continue;
2908
+ if (argIdx === 0)
2909
+ firstArg = child;
2910
+ else if (argIdx === 1)
2911
+ secondArg = child;
2912
+ argIdx++;
2913
+ }
2914
+ if (secondArg) {
2915
+ const receiver = firstArg ? extractReceiverName(firstArg) : undefined;
2916
+ const st = secondArg.type;
2917
+ if (st === 'string' || st === 'string_fragment') {
2918
+ const propName = secondArg.text.replace(/['"]/g, '');
2919
+ if (propName) {
2920
+ return {
2921
+ name: propName,
2922
+ line: callLine,
2923
+ dynamic: true,
2924
+ dynamicKind: 'computed-literal',
2925
+ keyExpr: secondArg.text,
2926
+ receiver,
2927
+ };
2928
+ }
2929
+ }
2930
+ if (st === 'identifier') {
2931
+ return {
2932
+ name: '<dynamic:computed-key>',
2933
+ line: callLine,
2934
+ dynamic: true,
2935
+ dynamicKind: 'computed-key',
2936
+ keyExpr: secondArg.text,
2937
+ receiver,
2938
+ };
2939
+ }
2940
+ }
2941
+ }
2942
+ return {
2943
+ name: '<dynamic:unresolved>',
2944
+ line: callLine,
2945
+ dynamic: true,
2946
+ dynamicKind: 'unresolved-dynamic',
2947
+ };
2948
+ }
2949
+ // .call()/.apply()/.bind() — this-rebinding; the wrapped function is the real callee.
2950
+ // When the object is a plain identifier (e.g. `f.call({})`), the target is statically
2951
+ // known so we emit a static call (no dynamic flag). This keeps parity with the native
2952
+ // Rust engine, which also resolves these as dyn=0, and prevents the dynZeroEdgeRows
2953
+ // upgrade path in emitDirectCallEdgesForCall from wrongly converting a dyn=0 edge
2954
+ // (emitted by a prior direct `f()` call) to dyn=1.
2955
+ // When the object is a member_expression (e.g. `obj.method.call({})`), we still mark
2956
+ // it dynamic/reflection because the inner callee requires a second resolution hop.
2601
2957
  if (propText === 'call' || propText === 'apply' || propText === 'bind') {
2602
2958
  if (obj && obj.type === 'identifier')
2603
- return { name: obj.text, line: callLine, dynamic: true };
2959
+ return { name: obj.text, line: callLine };
2604
2960
  if (obj && obj.type === 'member_expression') {
2605
2961
  const innerProp = obj.childForFieldName('property');
2606
2962
  if (innerProp)
2607
- return { name: innerProp.text, line: callLine, dynamic: true };
2963
+ return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
2608
2964
  }
2609
2965
  }
2610
- // Computed property: obj["method"]()
2966
+ // Computed string property: obj["method"]() — target is a literal; resolvable
2611
2967
  const propType = prop.type;
2612
2968
  if (propType === 'string' || propType === 'string_fragment') {
2613
2969
  const methodName = propText.replace(/['"]/g, '');
2614
2970
  if (methodName) {
2615
2971
  const receiver = extractReceiverName(obj);
2616
- return { name: methodName, line: callLine, dynamic: true, receiver };
2972
+ return {
2973
+ name: methodName,
2974
+ line: callLine,
2975
+ dynamic: true,
2976
+ dynamicKind: 'computed-literal',
2977
+ receiver,
2978
+ };
2617
2979
  }
2618
2980
  }
2619
2981
  const receiver = extractReceiverName(obj);
2620
2982
  return { name: propText, line: callLine, receiver };
2621
2983
  }
2622
- /** Extract call info from a subscript_expression function node (obj["method"]()). */
2984
+ /** Extract call info from a subscript_expression function node (obj[key]()). */
2623
2985
  function extractSubscriptCallInfo(fn, callNode) {
2624
2986
  const obj = fn.childForFieldName('object');
2625
2987
  const index = fn.childForFieldName('index');
@@ -2634,11 +2996,30 @@ function extractSubscriptCallInfo(fn, callNode) {
2634
2996
  name: methodName,
2635
2997
  line: nodeStartLine(callNode),
2636
2998
  dynamic: true,
2999
+ dynamicKind: 'computed-literal',
2637
3000
  receiver,
2638
3001
  };
2639
3002
  }
2640
3003
  }
2641
- return null;
3004
+ // obj[variable]() — key is a variable; may be resolvable via pts (RES-1), else flagged
3005
+ if (indexType === 'identifier') {
3006
+ const receiver = extractReceiverName(obj);
3007
+ return {
3008
+ name: '<dynamic:computed-key>',
3009
+ line: nodeStartLine(callNode),
3010
+ dynamic: true,
3011
+ dynamicKind: 'computed-key',
3012
+ keyExpr: index.text,
3013
+ receiver,
3014
+ };
3015
+ }
3016
+ // Any other index expression (binary, call, template with ${}…) — not statically resolvable
3017
+ return {
3018
+ name: '<dynamic:unresolved>',
3019
+ line: nodeStartLine(callNode),
3020
+ dynamic: true,
3021
+ dynamicKind: 'unresolved-dynamic',
3022
+ };
2642
3023
  }
2643
3024
  /**
2644
3025
  * Callee names that idiomatically accept callback references. Used to gate
@@ -2927,6 +3308,11 @@ function runCollectorWalk(rootNode, targets) {
2927
3308
  targets.newExpressions.push(name);
2928
3309
  break;
2929
3310
  }
3311
+ case 'decorator': {
3312
+ if (targets.calls)
3313
+ handleDecorator(node, targets.calls);
3314
+ break;
3315
+ }
2930
3316
  case 'field_definition':
2931
3317
  case 'public_field_definition':
2932
3318
  if (targets.classMemberDefs)