@optave/codegraph 3.12.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 (524) hide show
  1. package/README.md +83 -46
  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/audit.d.ts.map +1 -1
  51. package/dist/cli/commands/audit.js +2 -1
  52. package/dist/cli/commands/audit.js.map +1 -1
  53. package/dist/cli/commands/batch.d.ts.map +1 -1
  54. package/dist/cli/commands/batch.js +1 -0
  55. package/dist/cli/commands/batch.js.map +1 -1
  56. package/dist/cli/commands/build.d.ts.map +1 -1
  57. package/dist/cli/commands/build.js +6 -1
  58. package/dist/cli/commands/build.js.map +1 -1
  59. package/dist/cli/commands/config.d.ts +3 -0
  60. package/dist/cli/commands/config.d.ts.map +1 -0
  61. package/dist/cli/commands/config.js +275 -0
  62. package/dist/cli/commands/config.js.map +1 -0
  63. package/dist/cli/commands/roles.d.ts.map +1 -1
  64. package/dist/cli/commands/roles.js +6 -1
  65. package/dist/cli/commands/roles.js.map +1 -1
  66. package/dist/cli/commands/triage.js +1 -1
  67. package/dist/cli/commands/triage.js.map +1 -1
  68. package/dist/cli/index.d.ts.map +1 -1
  69. package/dist/cli/index.js +10 -0
  70. package/dist/cli/index.js.map +1 -1
  71. package/dist/cli/shared/options.d.ts +2 -1
  72. package/dist/cli/shared/options.d.ts.map +1 -1
  73. package/dist/cli/shared/options.js +11 -1
  74. package/dist/cli/shared/options.js.map +1 -1
  75. package/dist/cli/types.d.ts +2 -0
  76. package/dist/cli/types.d.ts.map +1 -1
  77. package/dist/db/better-sqlite3.d.ts +2 -1
  78. package/dist/db/better-sqlite3.d.ts.map +1 -1
  79. package/dist/db/better-sqlite3.js.map +1 -1
  80. package/dist/db/connection.d.ts +7 -1
  81. package/dist/db/connection.d.ts.map +1 -1
  82. package/dist/db/connection.js +20 -5
  83. package/dist/db/connection.js.map +1 -1
  84. package/dist/db/index.d.ts +1 -1
  85. package/dist/db/index.d.ts.map +1 -1
  86. package/dist/db/index.js +1 -1
  87. package/dist/db/index.js.map +1 -1
  88. package/dist/db/migrations.d.ts.map +1 -1
  89. package/dist/db/migrations.js +69 -1
  90. package/dist/db/migrations.js.map +1 -1
  91. package/dist/db/repository/build-stmts.d.ts.map +1 -1
  92. package/dist/db/repository/build-stmts.js +18 -0
  93. package/dist/db/repository/build-stmts.js.map +1 -1
  94. package/dist/db/repository/dataflow.d.ts +5 -0
  95. package/dist/db/repository/dataflow.d.ts.map +1 -1
  96. package/dist/db/repository/dataflow.js +14 -0
  97. package/dist/db/repository/dataflow.js.map +1 -1
  98. package/dist/db/repository/index.d.ts +1 -1
  99. package/dist/db/repository/index.d.ts.map +1 -1
  100. package/dist/db/repository/index.js +1 -1
  101. package/dist/db/repository/index.js.map +1 -1
  102. package/dist/db/repository/native-repository.d.ts.map +1 -1
  103. package/dist/db/repository/native-repository.js +47 -34
  104. package/dist/db/repository/native-repository.js.map +1 -1
  105. package/dist/domain/analysis/context.d.ts +2 -2
  106. package/dist/domain/analysis/dependencies.d.ts +2 -2
  107. package/dist/domain/analysis/diff-impact.d.ts +2 -2
  108. package/dist/domain/analysis/fn-impact.d.ts +3 -1
  109. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  110. package/dist/domain/analysis/fn-impact.js +4 -0
  111. package/dist/domain/analysis/fn-impact.js.map +1 -1
  112. package/dist/domain/analysis/implementations.d.ts +2 -2
  113. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  114. package/dist/domain/analysis/module-map.js +32 -5
  115. package/dist/domain/analysis/module-map.js.map +1 -1
  116. package/dist/domain/analysis/roles.d.ts +7 -1
  117. package/dist/domain/analysis/roles.d.ts.map +1 -1
  118. package/dist/domain/analysis/roles.js +16 -0
  119. package/dist/domain/analysis/roles.js.map +1 -1
  120. package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
  121. package/dist/domain/graph/builder/call-resolver.d.ts +29 -13
  122. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  123. package/dist/domain/graph/builder/call-resolver.js +125 -205
  124. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  125. package/dist/domain/graph/builder/cha.d.ts +9 -1
  126. package/dist/domain/graph/builder/cha.d.ts.map +1 -1
  127. package/dist/domain/graph/builder/cha.js +17 -2
  128. package/dist/domain/graph/builder/cha.js.map +1 -1
  129. package/dist/domain/graph/builder/context.d.ts +1 -0
  130. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  131. package/dist/domain/graph/builder/context.js.map +1 -1
  132. package/dist/domain/graph/builder/helpers.d.ts +24 -1
  133. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  134. package/dist/domain/graph/builder/helpers.js +174 -65
  135. package/dist/domain/graph/builder/helpers.js.map +1 -1
  136. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  137. package/dist/domain/graph/builder/incremental.js +166 -97
  138. package/dist/domain/graph/builder/incremental.js.map +1 -1
  139. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  140. package/dist/domain/graph/builder/pipeline.js +46 -5
  141. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  142. package/dist/domain/graph/builder/stages/build-edges.d.ts +0 -2
  143. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  144. package/dist/domain/graph/builder/stages/build-edges.js +554 -538
  145. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  146. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  147. package/dist/domain/graph/builder/stages/collect-files.js +10 -7
  148. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  149. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  150. package/dist/domain/graph/builder/stages/detect-changes.js +3 -2
  151. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  152. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  153. package/dist/domain/graph/builder/stages/finalize.js +4 -0
  154. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  155. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  156. package/dist/domain/graph/builder/stages/native-orchestrator.js +952 -343
  157. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  158. package/dist/domain/graph/builder/stages/resolve-imports.js +1 -1
  159. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  160. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
  161. package/dist/domain/graph/resolver/points-to.js +105 -57
  162. package/dist/domain/graph/resolver/points-to.js.map +1 -1
  163. package/dist/domain/graph/resolver/strategy.d.ts +61 -0
  164. package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
  165. package/dist/domain/graph/resolver/strategy.js +222 -0
  166. package/dist/domain/graph/resolver/strategy.js.map +1 -0
  167. package/dist/domain/graph/watcher.d.ts.map +1 -1
  168. package/dist/domain/graph/watcher.js +16 -9
  169. package/dist/domain/graph/watcher.js.map +1 -1
  170. package/dist/domain/parser.d.ts +16 -5
  171. package/dist/domain/parser.d.ts.map +1 -1
  172. package/dist/domain/parser.js +58 -17
  173. package/dist/domain/parser.js.map +1 -1
  174. package/dist/domain/queries.d.ts +1 -1
  175. package/dist/domain/queries.d.ts.map +1 -1
  176. package/dist/domain/queries.js +1 -1
  177. package/dist/domain/queries.js.map +1 -1
  178. package/dist/domain/wasm-worker-entry.js +13 -2
  179. package/dist/domain/wasm-worker-entry.js.map +1 -1
  180. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  181. package/dist/domain/wasm-worker-pool.js +26 -5
  182. package/dist/domain/wasm-worker-pool.js.map +1 -1
  183. package/dist/domain/wasm-worker-protocol.d.ts +8 -0
  184. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  185. package/dist/extractors/cpp.d.ts.map +1 -1
  186. package/dist/extractors/cpp.js +42 -1
  187. package/dist/extractors/cpp.js.map +1 -1
  188. package/dist/extractors/cuda.d.ts.map +1 -1
  189. package/dist/extractors/cuda.js +42 -1
  190. package/dist/extractors/cuda.js.map +1 -1
  191. package/dist/extractors/dart.js +48 -3
  192. package/dist/extractors/dart.js.map +1 -1
  193. package/dist/extractors/groovy.js +62 -3
  194. package/dist/extractors/groovy.js.map +1 -1
  195. package/dist/extractors/helpers.d.ts +15 -2
  196. package/dist/extractors/helpers.d.ts.map +1 -1
  197. package/dist/extractors/helpers.js +45 -1
  198. package/dist/extractors/helpers.js.map +1 -1
  199. package/dist/extractors/java.d.ts.map +1 -1
  200. package/dist/extractors/java.js +85 -8
  201. package/dist/extractors/java.js.map +1 -1
  202. package/dist/extractors/javascript.d.ts.map +1 -1
  203. package/dist/extractors/javascript.js +686 -169
  204. package/dist/extractors/javascript.js.map +1 -1
  205. package/dist/extractors/kotlin.js +58 -3
  206. package/dist/extractors/kotlin.js.map +1 -1
  207. package/dist/extractors/objc.js +25 -2
  208. package/dist/extractors/objc.js.map +1 -1
  209. package/dist/extractors/scala.js +62 -2
  210. package/dist/extractors/scala.js.map +1 -1
  211. package/dist/extractors/swift.js +52 -3
  212. package/dist/extractors/swift.js.map +1 -1
  213. package/dist/features/audit.js +26 -23
  214. package/dist/features/audit.js.map +1 -1
  215. package/dist/features/boundaries.d.ts.map +1 -1
  216. package/dist/features/boundaries.js +12 -9
  217. package/dist/features/boundaries.js.map +1 -1
  218. package/dist/features/cfg.d.ts.map +1 -1
  219. package/dist/features/cfg.js +25 -18
  220. package/dist/features/cfg.js.map +1 -1
  221. package/dist/features/check.d.ts.map +1 -1
  222. package/dist/features/check.js +18 -5
  223. package/dist/features/check.js.map +1 -1
  224. package/dist/features/communities.d.ts +4 -2
  225. package/dist/features/communities.d.ts.map +1 -1
  226. package/dist/features/communities.js +6 -4
  227. package/dist/features/communities.js.map +1 -1
  228. package/dist/features/dataflow.d.ts +60 -0
  229. package/dist/features/dataflow.d.ts.map +1 -1
  230. package/dist/features/dataflow.js +530 -6
  231. package/dist/features/dataflow.js.map +1 -1
  232. package/dist/features/manifesto.d.ts.map +1 -1
  233. package/dist/features/manifesto.js +59 -72
  234. package/dist/features/manifesto.js.map +1 -1
  235. package/dist/features/sequence.d.ts.map +1 -1
  236. package/dist/features/sequence.js +27 -22
  237. package/dist/features/sequence.js.map +1 -1
  238. package/dist/features/snapshot.d.ts.map +1 -1
  239. package/dist/features/snapshot.js +36 -28
  240. package/dist/features/snapshot.js.map +1 -1
  241. package/dist/features/structure-query.d.ts +1 -1
  242. package/dist/features/structure-query.d.ts.map +1 -1
  243. package/dist/features/structure-query.js +6 -6
  244. package/dist/features/structure-query.js.map +1 -1
  245. package/dist/features/structure.d.ts.map +1 -1
  246. package/dist/features/structure.js +150 -62
  247. package/dist/features/structure.js.map +1 -1
  248. package/dist/features/triage.d.ts.map +1 -1
  249. package/dist/features/triage.js +18 -11
  250. package/dist/features/triage.js.map +1 -1
  251. package/dist/graph/algorithms/bfs.d.ts +1 -1
  252. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  253. package/dist/graph/algorithms/bfs.js +14 -13
  254. package/dist/graph/algorithms/bfs.js.map +1 -1
  255. package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
  256. package/dist/graph/algorithms/tarjan.js +5 -0
  257. package/dist/graph/algorithms/tarjan.js.map +1 -1
  258. package/dist/graph/builders/dependency.js +28 -22
  259. package/dist/graph/builders/dependency.js.map +1 -1
  260. package/dist/graph/classifiers/roles.d.ts +10 -1
  261. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  262. package/dist/graph/classifiers/roles.js +60 -6
  263. package/dist/graph/classifiers/roles.js.map +1 -1
  264. package/dist/index.d.ts +1 -1
  265. package/dist/index.d.ts.map +1 -1
  266. package/dist/index.js +1 -1
  267. package/dist/index.js.map +1 -1
  268. package/dist/infrastructure/config.d.ts +87 -4
  269. package/dist/infrastructure/config.d.ts.map +1 -1
  270. package/dist/infrastructure/config.js +424 -22
  271. package/dist/infrastructure/config.js.map +1 -1
  272. package/dist/infrastructure/registry.d.ts +27 -7
  273. package/dist/infrastructure/registry.d.ts.map +1 -1
  274. package/dist/infrastructure/registry.js +79 -5
  275. package/dist/infrastructure/registry.js.map +1 -1
  276. package/dist/infrastructure/update-check.d.ts.map +1 -1
  277. package/dist/infrastructure/update-check.js +49 -31
  278. package/dist/infrastructure/update-check.js.map +1 -1
  279. package/dist/mcp/server.d.ts +2 -10
  280. package/dist/mcp/server.d.ts.map +1 -1
  281. package/dist/mcp/server.js.map +1 -1
  282. package/dist/mcp/tools/ast-query.d.ts +1 -1
  283. package/dist/mcp/tools/ast-query.d.ts.map +1 -1
  284. package/dist/mcp/tools/audit.d.ts +1 -1
  285. package/dist/mcp/tools/audit.d.ts.map +1 -1
  286. package/dist/mcp/tools/batch-query.d.ts +1 -1
  287. package/dist/mcp/tools/batch-query.d.ts.map +1 -1
  288. package/dist/mcp/tools/branch-compare.d.ts +1 -1
  289. package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
  290. package/dist/mcp/tools/brief.d.ts +1 -1
  291. package/dist/mcp/tools/brief.d.ts.map +1 -1
  292. package/dist/mcp/tools/cfg.d.ts +1 -1
  293. package/dist/mcp/tools/cfg.d.ts.map +1 -1
  294. package/dist/mcp/tools/check.d.ts +1 -1
  295. package/dist/mcp/tools/check.d.ts.map +1 -1
  296. package/dist/mcp/tools/co-changes.d.ts +1 -1
  297. package/dist/mcp/tools/co-changes.d.ts.map +1 -1
  298. package/dist/mcp/tools/code-owners.d.ts +1 -1
  299. package/dist/mcp/tools/code-owners.d.ts.map +1 -1
  300. package/dist/mcp/tools/communities.d.ts +1 -1
  301. package/dist/mcp/tools/communities.d.ts.map +1 -1
  302. package/dist/mcp/tools/complexity.d.ts +1 -1
  303. package/dist/mcp/tools/complexity.d.ts.map +1 -1
  304. package/dist/mcp/tools/context.d.ts +1 -1
  305. package/dist/mcp/tools/context.d.ts.map +1 -1
  306. package/dist/mcp/tools/dataflow.d.ts +1 -1
  307. package/dist/mcp/tools/dataflow.d.ts.map +1 -1
  308. package/dist/mcp/tools/diff-impact.d.ts +1 -1
  309. package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
  310. package/dist/mcp/tools/execution-flow.d.ts +1 -1
  311. package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
  312. package/dist/mcp/tools/export-graph.d.ts +1 -1
  313. package/dist/mcp/tools/export-graph.d.ts.map +1 -1
  314. package/dist/mcp/tools/file-deps.d.ts +1 -1
  315. package/dist/mcp/tools/file-deps.d.ts.map +1 -1
  316. package/dist/mcp/tools/file-exports.d.ts +1 -1
  317. package/dist/mcp/tools/file-exports.d.ts.map +1 -1
  318. package/dist/mcp/tools/find-cycles.d.ts +1 -1
  319. package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
  320. package/dist/mcp/tools/fn-impact.d.ts +1 -1
  321. package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
  322. package/dist/mcp/tools/impact-analysis.d.ts +1 -1
  323. package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
  324. package/dist/mcp/tools/implementations.d.ts +1 -1
  325. package/dist/mcp/tools/implementations.d.ts.map +1 -1
  326. package/dist/mcp/tools/index.d.ts +2 -5
  327. package/dist/mcp/tools/index.d.ts.map +1 -1
  328. package/dist/mcp/tools/index.js.map +1 -1
  329. package/dist/mcp/tools/interfaces.d.ts +1 -1
  330. package/dist/mcp/tools/interfaces.d.ts.map +1 -1
  331. package/dist/mcp/tools/list-functions.d.ts +1 -1
  332. package/dist/mcp/tools/list-functions.d.ts.map +1 -1
  333. package/dist/mcp/tools/list-repos.d.ts +1 -1
  334. package/dist/mcp/tools/list-repos.d.ts.map +1 -1
  335. package/dist/mcp/tools/module-map.d.ts +1 -1
  336. package/dist/mcp/tools/module-map.d.ts.map +1 -1
  337. package/dist/mcp/tools/node-roles.d.ts +1 -1
  338. package/dist/mcp/tools/node-roles.d.ts.map +1 -1
  339. package/dist/mcp/tools/path.d.ts +1 -1
  340. package/dist/mcp/tools/path.d.ts.map +1 -1
  341. package/dist/mcp/tools/query.d.ts +1 -1
  342. package/dist/mcp/tools/query.d.ts.map +1 -1
  343. package/dist/mcp/tools/semantic-search.d.ts +1 -1
  344. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  345. package/dist/mcp/tools/sequence.d.ts +1 -1
  346. package/dist/mcp/tools/sequence.d.ts.map +1 -1
  347. package/dist/mcp/tools/structure.d.ts +1 -1
  348. package/dist/mcp/tools/structure.d.ts.map +1 -1
  349. package/dist/mcp/tools/symbol-children.d.ts +1 -1
  350. package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
  351. package/dist/mcp/tools/triage.d.ts +1 -1
  352. package/dist/mcp/tools/triage.d.ts.map +1 -1
  353. package/dist/mcp/tools/where.d.ts +1 -1
  354. package/dist/mcp/tools/where.d.ts.map +1 -1
  355. package/dist/mcp/types.d.ts +19 -0
  356. package/dist/mcp/types.d.ts.map +1 -0
  357. package/dist/mcp/types.js +6 -0
  358. package/dist/mcp/types.js.map +1 -0
  359. package/dist/presentation/queries-cli/index.d.ts +1 -1
  360. package/dist/presentation/queries-cli/index.d.ts.map +1 -1
  361. package/dist/presentation/queries-cli/index.js +1 -1
  362. package/dist/presentation/queries-cli/index.js.map +1 -1
  363. package/dist/presentation/queries-cli/overview.d.ts +1 -0
  364. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  365. package/dist/presentation/queries-cli/overview.js +20 -1
  366. package/dist/presentation/queries-cli/overview.js.map +1 -1
  367. package/dist/presentation/queries-cli.d.ts +1 -1
  368. package/dist/presentation/queries-cli.d.ts.map +1 -1
  369. package/dist/presentation/queries-cli.js +1 -1
  370. package/dist/presentation/queries-cli.js.map +1 -1
  371. package/dist/presentation/structure.d.ts +1 -1
  372. package/dist/presentation/structure.d.ts.map +1 -1
  373. package/dist/presentation/structure.js +2 -2
  374. package/dist/presentation/structure.js.map +1 -1
  375. package/dist/presentation/viewer.d.ts.map +1 -1
  376. package/dist/presentation/viewer.js +45 -32
  377. package/dist/presentation/viewer.js.map +1 -1
  378. package/dist/shared/constants.d.ts +21 -0
  379. package/dist/shared/constants.d.ts.map +1 -1
  380. package/dist/shared/constants.js +25 -0
  381. package/dist/shared/constants.js.map +1 -1
  382. package/dist/shared/normalize.d.ts.map +1 -1
  383. package/dist/shared/normalize.js +12 -22
  384. package/dist/shared/normalize.js.map +1 -1
  385. package/dist/shared/paginate.d.ts +4 -17
  386. package/dist/shared/paginate.d.ts.map +1 -1
  387. package/dist/shared/paginate.js.map +1 -1
  388. package/dist/types.d.ts +113 -1
  389. package/dist/types.d.ts.map +1 -1
  390. package/grammars/tree-sitter-erlang.wasm +0 -0
  391. package/grammars/tree-sitter-gleam.wasm +0 -0
  392. package/package.json +7 -8
  393. package/src/ast-analysis/engine.ts +43 -63
  394. package/src/ast-analysis/rules/b2.ts +263 -0
  395. package/src/ast-analysis/rules/b3.ts +127 -0
  396. package/src/ast-analysis/rules/b4.ts +378 -0
  397. package/src/ast-analysis/rules/b5.ts +65 -0
  398. package/src/ast-analysis/rules/c.ts +157 -0
  399. package/src/ast-analysis/rules/index.ts +34 -0
  400. package/src/ast-analysis/rules/javascript.ts +3 -0
  401. package/src/ast-analysis/shared.ts +2 -0
  402. package/src/ast-analysis/visitor-utils.ts +5 -0
  403. package/src/ast-analysis/visitor.ts +82 -52
  404. package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
  405. package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
  406. package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
  407. package/src/cli/commands/audit.ts +2 -1
  408. package/src/cli/commands/batch.ts +1 -0
  409. package/src/cli/commands/build.ts +6 -1
  410. package/src/cli/commands/config.ts +353 -0
  411. package/src/cli/commands/roles.ts +6 -1
  412. package/src/cli/commands/triage.ts +1 -1
  413. package/src/cli/index.ts +10 -0
  414. package/src/cli/shared/options.ts +11 -1
  415. package/src/cli/types.ts +2 -0
  416. package/src/db/better-sqlite3.ts +5 -4
  417. package/src/db/connection.ts +23 -5
  418. package/src/db/index.ts +1 -0
  419. package/src/db/migrations.ts +69 -1
  420. package/src/db/repository/build-stmts.ts +30 -0
  421. package/src/db/repository/dataflow.ts +16 -0
  422. package/src/db/repository/index.ts +1 -1
  423. package/src/db/repository/native-repository.ts +56 -40
  424. package/src/domain/analysis/fn-impact.ts +4 -0
  425. package/src/domain/analysis/module-map.ts +38 -6
  426. package/src/domain/analysis/roles.ts +23 -0
  427. package/src/domain/graph/builder/call-resolver.ts +156 -218
  428. package/src/domain/graph/builder/cha.ts +18 -1
  429. package/src/domain/graph/builder/context.ts +1 -0
  430. package/src/domain/graph/builder/helpers.ts +205 -67
  431. package/src/domain/graph/builder/incremental.ts +249 -119
  432. package/src/domain/graph/builder/pipeline.ts +59 -6
  433. package/src/domain/graph/builder/stages/build-edges.ts +783 -652
  434. package/src/domain/graph/builder/stages/collect-files.ts +12 -6
  435. package/src/domain/graph/builder/stages/detect-changes.ts +4 -2
  436. package/src/domain/graph/builder/stages/finalize.ts +4 -0
  437. package/src/domain/graph/builder/stages/native-orchestrator.ts +1214 -398
  438. package/src/domain/graph/builder/stages/resolve-imports.ts +1 -1
  439. package/src/domain/graph/resolver/points-to.ts +182 -59
  440. package/src/domain/graph/resolver/strategy.ts +265 -0
  441. package/src/domain/graph/watcher.ts +19 -9
  442. package/src/domain/parser.ts +57 -16
  443. package/src/domain/queries.ts +1 -1
  444. package/src/domain/wasm-worker-entry.ts +13 -2
  445. package/src/domain/wasm-worker-pool.ts +29 -4
  446. package/src/domain/wasm-worker-protocol.ts +5 -0
  447. package/src/extractors/cpp.ts +44 -1
  448. package/src/extractors/cuda.ts +44 -1
  449. package/src/extractors/dart.ts +48 -3
  450. package/src/extractors/groovy.ts +62 -2
  451. package/src/extractors/helpers.ts +48 -2
  452. package/src/extractors/java.ts +88 -8
  453. package/src/extractors/javascript.ts +693 -167
  454. package/src/extractors/kotlin.ts +57 -3
  455. package/src/extractors/objc.ts +25 -1
  456. package/src/extractors/scala.ts +63 -1
  457. package/src/extractors/swift.ts +46 -3
  458. package/src/features/audit.ts +43 -34
  459. package/src/features/boundaries.ts +17 -9
  460. package/src/features/cfg.ts +31 -22
  461. package/src/features/check.ts +21 -5
  462. package/src/features/communities.ts +28 -19
  463. package/src/features/dataflow.ts +755 -6
  464. package/src/features/manifesto.ts +76 -75
  465. package/src/features/sequence.ts +29 -23
  466. package/src/features/snapshot.ts +36 -25
  467. package/src/features/structure-query.ts +7 -7
  468. package/src/features/structure.ts +185 -55
  469. package/src/features/triage.ts +28 -15
  470. package/src/graph/algorithms/bfs.ts +13 -12
  471. package/src/graph/algorithms/tarjan.ts +5 -0
  472. package/src/graph/builders/dependency.ts +35 -23
  473. package/src/graph/classifiers/roles.ts +74 -7
  474. package/src/index.ts +5 -1
  475. package/src/infrastructure/config.ts +511 -23
  476. package/src/infrastructure/registry.ts +117 -12
  477. package/src/infrastructure/update-check.ts +55 -33
  478. package/src/mcp/server.ts +2 -8
  479. package/src/mcp/tools/ast-query.ts +1 -1
  480. package/src/mcp/tools/audit.ts +1 -1
  481. package/src/mcp/tools/batch-query.ts +1 -1
  482. package/src/mcp/tools/branch-compare.ts +1 -1
  483. package/src/mcp/tools/brief.ts +1 -1
  484. package/src/mcp/tools/cfg.ts +1 -1
  485. package/src/mcp/tools/check.ts +1 -1
  486. package/src/mcp/tools/co-changes.ts +1 -1
  487. package/src/mcp/tools/code-owners.ts +1 -1
  488. package/src/mcp/tools/communities.ts +1 -1
  489. package/src/mcp/tools/complexity.ts +1 -1
  490. package/src/mcp/tools/context.ts +1 -1
  491. package/src/mcp/tools/dataflow.ts +1 -1
  492. package/src/mcp/tools/diff-impact.ts +1 -1
  493. package/src/mcp/tools/execution-flow.ts +1 -1
  494. package/src/mcp/tools/export-graph.ts +1 -1
  495. package/src/mcp/tools/file-deps.ts +1 -1
  496. package/src/mcp/tools/file-exports.ts +1 -1
  497. package/src/mcp/tools/find-cycles.ts +1 -1
  498. package/src/mcp/tools/fn-impact.ts +1 -1
  499. package/src/mcp/tools/impact-analysis.ts +1 -1
  500. package/src/mcp/tools/implementations.ts +1 -1
  501. package/src/mcp/tools/index.ts +2 -5
  502. package/src/mcp/tools/interfaces.ts +1 -1
  503. package/src/mcp/tools/list-functions.ts +1 -1
  504. package/src/mcp/tools/list-repos.ts +1 -1
  505. package/src/mcp/tools/module-map.ts +1 -1
  506. package/src/mcp/tools/node-roles.ts +1 -1
  507. package/src/mcp/tools/path.ts +1 -1
  508. package/src/mcp/tools/query.ts +1 -1
  509. package/src/mcp/tools/semantic-search.ts +1 -1
  510. package/src/mcp/tools/sequence.ts +1 -1
  511. package/src/mcp/tools/structure.ts +1 -1
  512. package/src/mcp/tools/symbol-children.ts +1 -1
  513. package/src/mcp/tools/triage.ts +1 -1
  514. package/src/mcp/tools/where.ts +1 -1
  515. package/src/mcp/types.ts +21 -0
  516. package/src/presentation/queries-cli/index.ts +1 -1
  517. package/src/presentation/queries-cli/overview.ts +35 -1
  518. package/src/presentation/queries-cli.ts +1 -0
  519. package/src/presentation/structure.ts +3 -3
  520. package/src/presentation/viewer.ts +98 -87
  521. package/src/shared/constants.ts +26 -0
  522. package/src/shared/normalize.ts +13 -22
  523. package/src/shared/paginate.ts +4 -18
  524. package/src/types.ts +127 -1
@@ -13,6 +13,7 @@ interface ParamInfo {
13
13
 
14
14
  interface LanguageRules {
15
15
  nameField: string;
16
+ nameExtractor?: ((node: TreeSitterNode) => string | null) | null;
16
17
  varAssignedFnParent?: string;
17
18
  pairFnParent?: string;
18
19
  assignmentFnParent?: string;
@@ -47,6 +48,10 @@ export function truncate(str: string, max = 120): string {
47
48
  */
48
49
  export function functionName(fnNode: TreeSitterNode | null, rules: LanguageRules): string | null {
49
50
  if (!fnNode) return null;
51
+ if (rules.nameExtractor) {
52
+ const extracted = rules.nameExtractor(fnNode);
53
+ if (extracted) return extracted;
54
+ }
50
55
  const nameNode = fnNode.childForFieldName(rules.nameField);
51
56
  if (nameNode) return nameNode.text;
52
57
 
@@ -152,24 +152,18 @@ function collectResults(visitors: Visitor[]): WalkResults {
152
152
  return results;
153
153
  }
154
154
 
155
- /**
156
- * Walk an AST root with multiple visitors in a single DFS pass.
157
- *
158
- * @param {object} rootNode - tree-sitter root node to walk
159
- * @param {Visitor[]} visitors - array of visitor objects
160
- * @param {string} langId - language identifier
161
- * @param {object} [options]
162
- * @param {Set} [options.functionNodeTypes] - set of node types that are function boundaries
163
- * @param {Set} [options.nestingNodeTypes] - set of node types that increase nesting depth
164
- * @param {function} [options.getFunctionName] - (funcNode) => string|null
165
- * @returns {object} Map of visitor.name finish() result
166
- */
167
- export function walkWithVisitors(
168
- rootNode: TreeSitterNode,
169
- visitors: Visitor[],
170
- langId: string,
171
- options: WalkOptions = {},
172
- ): WalkResults {
155
+ interface WalkState {
156
+ visitors: Visitor[];
157
+ allFuncTypes: Set<string>;
158
+ nestingNodeTypes: Set<string>;
159
+ getFunctionName: (node: TreeSitterNode) => string | null;
160
+ context: VisitorContext;
161
+ scopeStack: ScopeEntry[];
162
+ skipDepths: Map<number, number>;
163
+ }
164
+
165
+ /** Build walk state from options: resolve function types, init visitors, create shared context. */
166
+ function buildWalkState(visitors: Visitor[], langId: string, options: WalkOptions): WalkState {
173
167
  const {
174
168
  functionNodeTypes = new Set<string>(),
175
169
  nestingNodeTypes = new Set<string>(),
@@ -179,7 +173,6 @@ export function walkWithVisitors(
179
173
  const allFuncTypes = mergeFunctionNodeTypes(visitors, functionNodeTypes);
180
174
  initVisitors(visitors, langId);
181
175
 
182
- // Shared context object (mutated during walk)
183
176
  const scopeStack: ScopeEntry[] = [];
184
177
  const context: VisitorContext = {
185
178
  nestingLevel: 0,
@@ -188,49 +181,86 @@ export function walkWithVisitors(
188
181
  scopeStack,
189
182
  };
190
183
 
191
- const skipDepths = new Map<number, number>();
184
+ return {
185
+ visitors,
186
+ allFuncTypes,
187
+ nestingNodeTypes,
188
+ getFunctionName,
189
+ context,
190
+ scopeStack,
191
+ skipDepths: new Map<number, number>(),
192
+ };
193
+ }
192
194
 
193
- function walk(node: TreeSitterNode | null, depth: number): void {
194
- if (!node) return;
195
- if (depth > MAX_WALK_DEPTH) {
196
- debug(`walkWithVisitors: AST depth limit (${MAX_WALK_DEPTH}) hit — subtree truncated`);
197
- return;
198
- }
195
+ /** Single DFS step: dispatch enter/exit hooks and recurse into children. */
196
+ function walkNode(state: WalkState, node: TreeSitterNode | null, depth: number): void {
197
+ if (!node) return;
198
+ if (depth > MAX_WALK_DEPTH) {
199
+ debug(`walkWithVisitors: AST depth limit (${MAX_WALK_DEPTH}) hit — subtree truncated`);
200
+ return;
201
+ }
199
202
 
200
- const type = node.type;
201
- const isFuncBoundary = allFuncTypes.has(type);
202
- let funcName: string | null = null;
203
+ const {
204
+ visitors,
205
+ allFuncTypes,
206
+ nestingNodeTypes,
207
+ getFunctionName,
208
+ context,
209
+ scopeStack,
210
+ skipDepths,
211
+ } = state;
212
+ const type = node.type;
213
+ const isFuncBoundary = allFuncTypes.has(type);
214
+ let funcName: string | null = null;
203
215
 
204
- if (isFuncBoundary) {
205
- funcName = getFunctionName(node);
206
- context.currentFunction = node;
207
- scopeStack.push({ funcName, funcNode: node, params: new Map(), locals: new Map() });
208
- dispatchEnterFunction(visitors, skipDepths, node, funcName, context, depth);
209
- }
216
+ if (isFuncBoundary) {
217
+ funcName = getFunctionName(node);
218
+ context.currentFunction = node;
219
+ scopeStack.push({ funcName, funcNode: node, params: new Map(), locals: new Map() });
220
+ dispatchEnterFunction(visitors, skipDepths, node, funcName, context, depth);
221
+ }
210
222
 
211
- dispatchEnterNode(visitors, skipDepths, node, context, depth);
223
+ dispatchEnterNode(visitors, skipDepths, node, context, depth);
212
224
 
213
- const addsNesting = nestingNodeTypes.has(type);
214
- if (addsNesting) context.nestingLevel++;
225
+ const addsNesting = nestingNodeTypes.has(type);
226
+ if (addsNesting) context.nestingLevel++;
215
227
 
216
- for (let i = 0; i < node.childCount; i++) {
217
- walk(node.child(i), depth + 1);
218
- }
228
+ for (let i = 0; i < node.childCount; i++) {
229
+ walkNode(state, node.child(i), depth + 1);
230
+ }
219
231
 
220
- if (addsNesting) context.nestingLevel--;
232
+ if (addsNesting) context.nestingLevel--;
221
233
 
222
- dispatchExitNode(visitors, skipDepths, node, context, depth);
223
- clearSkipFlags(skipDepths, visitors.length, depth);
234
+ dispatchExitNode(visitors, skipDepths, node, context, depth);
235
+ clearSkipFlags(skipDepths, visitors.length, depth);
224
236
 
225
- if (isFuncBoundary) {
226
- dispatchExitFunction(visitors, skipDepths, node, funcName, context, depth);
227
- scopeStack.pop();
228
- context.currentFunction =
229
- scopeStack.length > 0 ? scopeStack[scopeStack.length - 1]!.funcNode : null;
230
- }
237
+ if (isFuncBoundary) {
238
+ dispatchExitFunction(visitors, skipDepths, node, funcName, context, depth);
239
+ scopeStack.pop();
240
+ context.currentFunction =
241
+ scopeStack.length > 0 ? scopeStack[scopeStack.length - 1]!.funcNode : null;
231
242
  }
243
+ }
232
244
 
233
- walk(rootNode, 0);
234
-
245
+ /**
246
+ * Walk an AST root with multiple visitors in a single DFS pass.
247
+ *
248
+ * @param {object} rootNode - tree-sitter root node to walk
249
+ * @param {Visitor[]} visitors - array of visitor objects
250
+ * @param {string} langId - language identifier
251
+ * @param {object} [options]
252
+ * @param {Set} [options.functionNodeTypes] - set of node types that are function boundaries
253
+ * @param {Set} [options.nestingNodeTypes] - set of node types that increase nesting depth
254
+ * @param {function} [options.getFunctionName] - (funcNode) => string|null
255
+ * @returns {object} Map of visitor.name → finish() result
256
+ */
257
+ export function walkWithVisitors(
258
+ rootNode: TreeSitterNode,
259
+ visitors: Visitor[],
260
+ langId: string,
261
+ options: WalkOptions = {},
262
+ ): WalkResults {
263
+ const state = buildWalkState(visitors, langId, options);
264
+ walkNode(state, rootNode, 0);
235
265
  return collectResults(visitors);
236
266
  }
@@ -27,97 +27,28 @@ import { processTryCatch } from './cfg-try-catch.js';
27
27
 
28
28
  export type { CfgBlockInternal } from './cfg-shared.js';
29
29
 
30
- function processStatements(
30
+ // ─── Statement handler dispatch ─────────────────────────────────────────
31
+
32
+ type BoundProcessStatements = (
31
33
  stmts: TreeSitterNode[],
32
34
  currentBlock: CfgBlockInternal,
33
35
  S: FuncState,
34
- cfgRules: AnyRules,
35
- ): CfgBlockInternal | null {
36
- let cur: CfgBlockInternal | null = currentBlock;
37
- for (const stmt of stmts) {
38
- if (!cur) break;
39
- cur = processStatement(stmt, cur, S, cfgRules);
40
- }
41
- return cur;
42
- }
36
+ ) => CfgBlockInternal | null;
43
37
 
44
- function processStatement(
45
- stmt: TreeSitterNode,
38
+ type StatementHandler = (
39
+ node: TreeSitterNode,
46
40
  currentBlock: CfgBlockInternal,
47
41
  S: FuncState,
48
42
  cfgRules: AnyRules,
49
- ): CfgBlockInternal | null {
50
- if (!stmt || !currentBlock) return currentBlock;
51
-
52
- const effNode = effectiveNode(stmt, cfgRules);
53
- const type = effNode.type;
43
+ processStmts: BoundProcessStatements,
44
+ ) => CfgBlockInternal | null;
54
45
 
55
- if (type === cfgRules.labeledNode) {
56
- return processLabeled(effNode, currentBlock, S, cfgRules);
57
- }
58
- if (isIfNode(type, cfgRules) || (cfgRules.unlessNode && type === cfgRules.unlessNode)) {
59
- return processIf(effNode, currentBlock, S, cfgRules, processStatements);
60
- }
61
- if (isForNode(type, cfgRules)) {
62
- return processForLoop(effNode, currentBlock, S, cfgRules, processStatements);
63
- }
64
- if (isWhileNode(type, cfgRules) || (cfgRules.untilNode && type === cfgRules.untilNode)) {
65
- return processWhileLoop(effNode, currentBlock, S, cfgRules, processStatements);
66
- }
67
- if (cfgRules.doNode && type === cfgRules.doNode) {
68
- return processDoWhileLoop(effNode, currentBlock, S, cfgRules, processStatements);
69
- }
70
- if (cfgRules.infiniteLoopNode && type === cfgRules.infiniteLoopNode) {
71
- return processInfiniteLoop(effNode, currentBlock, S, cfgRules, processStatements);
72
- }
73
- if (isSwitchNode(type, cfgRules)) {
74
- return processSwitch(effNode, currentBlock, S, cfgRules, processStatements);
75
- }
76
- if (cfgRules.tryNode && type === cfgRules.tryNode) {
77
- return processTryCatch(effNode, currentBlock, S, cfgRules, processStatements);
78
- }
79
- if (type === cfgRules.returnNode) {
80
- currentBlock.endLine = effNode.startPosition.row + 1;
81
- S.addEdge(currentBlock, S.exitBlock, 'return');
82
- return null;
83
- }
84
- if (type === cfgRules.throwNode) {
85
- currentBlock.endLine = effNode.startPosition.row + 1;
86
- S.addEdge(currentBlock, S.exitBlock, 'exception');
87
- return null;
88
- }
89
- if (type === cfgRules.breakNode) {
90
- return processBreak(effNode, currentBlock, S);
91
- }
92
- if (type === cfgRules.continueNode) {
93
- return processContinue(effNode, currentBlock, S);
94
- }
95
-
96
- if (!currentBlock.startLine) {
97
- currentBlock.startLine = stmt.startPosition.row + 1;
98
- }
99
- currentBlock.endLine = stmt.endPosition.row + 1;
100
- return currentBlock;
46
+ interface StatementEntry {
47
+ match: (type: string) => boolean;
48
+ handle: StatementHandler;
101
49
  }
102
50
 
103
- function processLabeled(
104
- node: TreeSitterNode,
105
- currentBlock: CfgBlockInternal,
106
- S: FuncState,
107
- cfgRules: AnyRules,
108
- ): CfgBlockInternal | null {
109
- const labelNode = node.childForFieldName('label');
110
- const labelName = labelNode ? labelNode.text : null;
111
- const body = node.childForFieldName('body');
112
- if (body && labelName) {
113
- const labelCtx: LabelCtx = { headerBlock: null, exitBlock: null };
114
- S.labelMap.set(labelName, labelCtx);
115
- const result = processStatement(body, currentBlock, S, cfgRules);
116
- S.labelMap.delete(labelName);
117
- return result;
118
- }
119
- return currentBlock;
120
- }
51
+ // ─── Helpers that do not depend on the dispatch closure ─────────────────
121
52
 
122
53
  function processBreak(
123
54
  node: TreeSitterNode,
@@ -165,7 +96,186 @@ function processContinue(
165
96
  return currentBlock;
166
97
  }
167
98
 
168
- function processFunctionBody(funcNode: TreeSitterNode, S: FuncState, cfgRules: AnyRules): void {
99
+ // ─── Dispatch table builder ──────────────────────────────────────────────
100
+
101
+ // ─── Terminal statement handlers (no processStatements dependency) ───────
102
+
103
+ function handleReturn(n: TreeSitterNode, b: CfgBlockInternal, S: FuncState): null {
104
+ b.endLine = n.startPosition.row + 1;
105
+ S.addEdge(b, S.exitBlock, 'return');
106
+ return null;
107
+ }
108
+
109
+ function handleThrow(n: TreeSitterNode, b: CfgBlockInternal, S: FuncState): null {
110
+ b.endLine = n.startPosition.row + 1;
111
+ S.addEdge(b, S.exitBlock, 'exception');
112
+ return null;
113
+ }
114
+
115
+ /**
116
+ * Lookup structure for statement dispatch.
117
+ * - `map`: O(1) lookup for single-type matchers (one concrete node-type string per handler).
118
+ * - `fallback`: short linear scan for multi-predicate entries (if/for/while/switch rules that
119
+ * match multiple concrete type strings depending on the language).
120
+ */
121
+ interface StatementDispatch {
122
+ map: Map<string, StatementHandler>;
123
+ fallback: StatementEntry[];
124
+ }
125
+
126
+ /**
127
+ * Build a dispatch table for statement node types from cfgRules.
128
+ * Built once per createCfgVisitor call; optional entries (doNode, infiniteLoopNode,
129
+ * tryNode) are included only when the language rules define them.
130
+ *
131
+ * Single-type matchers go into a Map for O(1) lookup; multi-predicate entries
132
+ * (isIfNode, isForNode, isWhileNode, isSwitchNode) stay in a short fallback array
133
+ * scanned only on a map miss.
134
+ */
135
+ function buildStatementDispatch(
136
+ cfgRules: AnyRules,
137
+ processLabeledFn: (
138
+ n: TreeSitterNode,
139
+ b: CfgBlockInternal,
140
+ S: FuncState,
141
+ ) => CfgBlockInternal | null,
142
+ ): StatementDispatch {
143
+ const map = new Map<string, StatementHandler>();
144
+
145
+ // Single-type required matchers
146
+ if (cfgRules.labeledNode) map.set(cfgRules.labeledNode, (n, b, S) => processLabeledFn(n, b, S));
147
+ if (cfgRules.returnNode) map.set(cfgRules.returnNode, handleReturn);
148
+ if (cfgRules.throwNode) map.set(cfgRules.throwNode, handleThrow);
149
+ if (cfgRules.breakNode) map.set(cfgRules.breakNode, (n, b, S) => processBreak(n, b, S));
150
+ if (cfgRules.continueNode) map.set(cfgRules.continueNode, (n, b, S) => processContinue(n, b, S));
151
+
152
+ // Single-type optional matchers
153
+ if (cfgRules.doNode)
154
+ map.set(cfgRules.doNode, (n, b, S, r, ps) => processDoWhileLoop(n, b, S, r, ps));
155
+ if (cfgRules.infiniteLoopNode)
156
+ map.set(cfgRules.infiniteLoopNode, (n, b, S, r, ps) => processInfiniteLoop(n, b, S, r, ps));
157
+ if (cfgRules.tryNode)
158
+ map.set(cfgRules.tryNode, (n, b, S, r, ps) => processTryCatch(n, b, S, r, ps));
159
+
160
+ // Multi-predicate entries that can match several concrete type strings per language;
161
+ // also handles unlessNode/untilNode aliases for if/while which may collide with other
162
+ // single-type map keys.
163
+ const fallback: StatementEntry[] = [
164
+ {
165
+ match: (t) => isIfNode(t, cfgRules) || (!!cfgRules.unlessNode && t === cfgRules.unlessNode),
166
+ handle: (n, b, S, r, ps) => processIf(n, b, S, r, ps),
167
+ },
168
+ {
169
+ match: (t) => isForNode(t, cfgRules),
170
+ handle: (n, b, S, r, ps) => processForLoop(n, b, S, r, ps),
171
+ },
172
+ {
173
+ match: (t) => isWhileNode(t, cfgRules) || (!!cfgRules.untilNode && t === cfgRules.untilNode),
174
+ handle: (n, b, S, r, ps) => processWhileLoop(n, b, S, r, ps),
175
+ },
176
+ {
177
+ match: (t) => isSwitchNode(t, cfgRules),
178
+ handle: (n, b, S, r, ps) => processSwitch(n, b, S, r, ps),
179
+ },
180
+ ];
181
+
182
+ return { map, fallback };
183
+ }
184
+
185
+ // ─── Bound statement processors ──────────────────────────────────────────
186
+
187
+ /**
188
+ * Build {processStatement, processStatements} bound to cfgRules and a
189
+ * pre-built dispatch table. The two functions are mutually recursive via
190
+ * the closure — no cfgRules arguments needed at call sites inside the visitor.
191
+ */
192
+ function buildStatementProcessors(cfgRules: AnyRules): {
193
+ processStatement: (
194
+ stmt: TreeSitterNode,
195
+ currentBlock: CfgBlockInternal,
196
+ S: FuncState,
197
+ ) => CfgBlockInternal | null;
198
+ processStatements: BoundProcessStatements;
199
+ } {
200
+ // processLabeled needs processStatement from this closure, so we forward-declare
201
+ // it and patch it in after processStatement is defined.
202
+ let processStatementRef: (
203
+ stmt: TreeSitterNode,
204
+ block: CfgBlockInternal,
205
+ S: FuncState,
206
+ ) => CfgBlockInternal | null;
207
+
208
+ function processLabeled(
209
+ node: TreeSitterNode,
210
+ currentBlock: CfgBlockInternal,
211
+ S: FuncState,
212
+ ): CfgBlockInternal | null {
213
+ const labelNode = node.childForFieldName('label');
214
+ const labelName = labelNode ? labelNode.text : null;
215
+ const body = node.childForFieldName('body');
216
+ if (body && labelName) {
217
+ const labelCtx: LabelCtx = { headerBlock: null, exitBlock: null };
218
+ S.labelMap.set(labelName, labelCtx);
219
+ const result = processStatementRef(body, currentBlock, S);
220
+ S.labelMap.delete(labelName);
221
+ return result;
222
+ }
223
+ return currentBlock;
224
+ }
225
+
226
+ const dispatch = buildStatementDispatch(cfgRules, processLabeled);
227
+
228
+ function processStatement(
229
+ stmt: TreeSitterNode,
230
+ currentBlock: CfgBlockInternal,
231
+ S: FuncState,
232
+ ): CfgBlockInternal | null {
233
+ if (!stmt || !currentBlock) return currentBlock;
234
+
235
+ const effNode = effectiveNode(stmt, cfgRules);
236
+ const type = effNode.type;
237
+
238
+ // O(1) map lookup first; fall back to the short multi-predicate array on a miss
239
+ const mapHandler = dispatch.map.get(type);
240
+ if (mapHandler) return mapHandler(effNode, currentBlock, S, cfgRules, processStatements);
241
+ const fallbackEntry = dispatch.fallback.find((e) => e.match(type));
242
+ if (fallbackEntry)
243
+ return fallbackEntry.handle(effNode, currentBlock, S, cfgRules, processStatements);
244
+
245
+ if (!currentBlock.startLine) {
246
+ currentBlock.startLine = stmt.startPosition.row + 1;
247
+ }
248
+ currentBlock.endLine = stmt.endPosition.row + 1;
249
+ return currentBlock;
250
+ }
251
+
252
+ // Wire the forward reference so processLabeled can call processStatement
253
+ processStatementRef = processStatement;
254
+
255
+ function processStatements(
256
+ stmts: TreeSitterNode[],
257
+ currentBlock: CfgBlockInternal,
258
+ S: FuncState,
259
+ ): CfgBlockInternal | null {
260
+ let cur: CfgBlockInternal | null = currentBlock;
261
+ for (const stmt of stmts) {
262
+ if (!cur) break;
263
+ cur = processStatement(stmt, cur, S);
264
+ }
265
+ return cur;
266
+ }
267
+
268
+ return { processStatement, processStatements };
269
+ }
270
+
271
+ // ─── Function body walker ────────────────────────────────────────────────
272
+
273
+ function processFunctionBody(
274
+ funcNode: TreeSitterNode,
275
+ S: FuncState,
276
+ cfgRules: AnyRules,
277
+ processStatements: BoundProcessStatements,
278
+ ): void {
169
279
  const body = funcNode.childForFieldName('body');
170
280
  if (!body) {
171
281
  S.blocks.length = 2;
@@ -194,18 +304,22 @@ function processFunctionBody(funcNode: TreeSitterNode, S: FuncState, cfgRules: A
194
304
  }
195
305
 
196
306
  const firstBody = S.blocks[2]!;
197
- const lastBlock = processStatements(stmts, firstBody, S, cfgRules);
307
+ const lastBlock = processStatements(stmts, firstBody, S);
198
308
  if (lastBlock) {
199
309
  S.addEdge(lastBlock, S.exitBlock, 'fallthrough');
200
310
  }
201
311
  S.currentBlock = null;
202
312
  }
203
313
 
314
+ // ─── Public visitor factory ───────────────────────────────────────────────
315
+
204
316
  export function createCfgVisitor(cfgRules: AnyRules): Visitor {
205
317
  const funcStateStack: FuncState[] = [];
206
318
  let S: FuncState | null = null;
207
319
  const results: CFGResultInternal[] = [];
208
320
 
321
+ const { processStatements } = buildStatementProcessors(cfgRules);
322
+
209
323
  return {
210
324
  name: 'cfg',
211
325
  functionNodeTypes: cfgRules.functionNodes,
@@ -218,7 +332,7 @@ export function createCfgVisitor(cfgRules: AnyRules): Visitor {
218
332
  if (S) funcStateStack.push(S);
219
333
  S = makeFuncState();
220
334
  S.funcNode = funcNode;
221
- processFunctionBody(funcNode, S, cfgRules);
335
+ processFunctionBody(funcNode, S, cfgRules, processStatements);
222
336
  },
223
337
 
224
338
  exitFunction(
@@ -203,6 +203,43 @@ function classifyNode(
203
203
  if (cRules.caseNodes.has(type) && node.childCount > 0) acc.cyclomatic++;
204
204
  }
205
205
 
206
+ /**
207
+ * Compute the effective nesting level for complexity classification.
208
+ *
209
+ * In file-level mode, funcDepth starts at 0 for the active function.
210
+ * In function-level mode, funcDepth starts at 1 for the root function
211
+ * (since enterFunction always increments it). Subtract 1 so the root
212
+ * function contributes 0 nesting and each nested level adds +1, matching
213
+ * the Rust engine's behavior.
214
+ */
215
+ function computeEffectiveNesting(
216
+ contextNesting: number,
217
+ funcDepth: number,
218
+ nestingAdjust: number,
219
+ fileLevelWalk: boolean,
220
+ ): number {
221
+ const funcNesting = fileLevelWalk ? funcDepth : Math.max(0, funcDepth - 1);
222
+ return contextNesting + funcNesting - nestingAdjust;
223
+ }
224
+
225
+ /**
226
+ * If this node is an else-if that the walker treats as a nesting node but
227
+ * the DFS engine would NOT increment nesting for, track it so children see
228
+ * the correct (non-inflated) nesting level.
229
+ */
230
+ function trackElseIfNestingAdjust(
231
+ node: TreeSitterNode,
232
+ cRules: AnyRules,
233
+ nestingAdjust: number,
234
+ adjustNodeIds: Set<number>,
235
+ ): number {
236
+ if (cRules.nestingNodes.has(node.type) && isElseIfNonNesting(node, node.type, cRules)) {
237
+ adjustNodeIds.add(node.id);
238
+ return nestingAdjust + 1;
239
+ }
240
+ return nestingAdjust;
241
+ }
242
+
206
243
  export function createComplexityVisitor(
207
244
  cRules: AnyRules,
208
245
  hRules?: AnyRules | null,
@@ -265,23 +302,14 @@ export function createComplexityVisitor(
265
302
  enterNode(node: TreeSitterNode, context: VisitorContext): EnterNodeResult | undefined {
266
303
  if (fileLevelWalk && !activeFuncNode) return;
267
304
 
268
- // In file-level mode, funcDepth starts at 0 for the active function.
269
- // In function-level mode, funcDepth starts at 1 for the root function
270
- // (since enterFunction always increments it). Nested functions add +1
271
- // each level — subtract 1 so the root function contributes 0 nesting
272
- // and each nested level adds +1, matching the Rust engine's behavior.
273
- const funcNesting = fileLevelWalk ? funcDepth : Math.max(0, funcDepth - 1);
274
- const nestingLevel = context.nestingLevel + funcNesting - nestingAdjust;
305
+ const nestingLevel = computeEffectiveNesting(
306
+ context.nestingLevel,
307
+ funcDepth,
308
+ nestingAdjust,
309
+ fileLevelWalk,
310
+ );
275
311
  classifyNode(node, nestingLevel, cRules, hRules, acc);
276
-
277
- // If this is an else-if if_statement that the walker will treat as a
278
- // nesting node (incrementing context.nestingLevel for children), but
279
- // the DFS walk would NOT increment nesting for, compensate by bumping
280
- // nestingAdjust so children see the correct level.
281
- if (cRules.nestingNodes.has(node.type) && isElseIfNonNesting(node, node.type, cRules)) {
282
- nestingAdjust++;
283
- adjustNodeIds.add(node.id);
284
- }
312
+ nestingAdjust = trackElseIfNestingAdjust(node, cRules, nestingAdjust, adjustNodeIds);
285
313
  },
286
314
 
287
315
  exitNode(node: TreeSitterNode): void {