@optave/codegraph 3.7.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +28 -16
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +195 -30
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/metrics.d.ts +0 -3
  6. package/dist/ast-analysis/metrics.d.ts.map +1 -1
  7. package/dist/ast-analysis/metrics.js +30 -13
  8. package/dist/ast-analysis/metrics.js.map +1 -1
  9. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  10. package/dist/ast-analysis/rules/javascript.js +0 -1
  11. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  12. package/dist/ast-analysis/shared.d.ts.map +1 -1
  13. package/dist/ast-analysis/shared.js +24 -19
  14. package/dist/ast-analysis/shared.js.map +1 -1
  15. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitor-utils.js +55 -39
  17. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  18. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  19. package/dist/ast-analysis/visitor.js +91 -70
  20. package/dist/ast-analysis/visitor.js.map +1 -1
  21. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  22. package/dist/ast-analysis/visitors/ast-store-visitor.js +52 -129
  23. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  24. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  25. package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
  26. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  27. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  28. package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
  29. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  30. package/dist/cli/commands/ast.js +2 -2
  31. package/dist/cli/commands/ast.js.map +1 -1
  32. package/dist/cli/commands/watch.d.ts.map +1 -1
  33. package/dist/cli/commands/watch.js +16 -2
  34. package/dist/cli/commands/watch.js.map +1 -1
  35. package/dist/db/connection.d.ts.map +1 -1
  36. package/dist/db/connection.js +29 -26
  37. package/dist/db/connection.js.map +1 -1
  38. package/dist/db/query-builder.d.ts.map +1 -1
  39. package/dist/db/query-builder.js +16 -5
  40. package/dist/db/query-builder.js.map +1 -1
  41. package/dist/db/repository/base.d.ts +10 -0
  42. package/dist/db/repository/base.d.ts.map +1 -1
  43. package/dist/db/repository/base.js +17 -0
  44. package/dist/db/repository/base.js.map +1 -1
  45. package/dist/db/repository/native-repository.d.ts +6 -1
  46. package/dist/db/repository/native-repository.d.ts.map +1 -1
  47. package/dist/db/repository/native-repository.js +77 -1
  48. package/dist/db/repository/native-repository.js.map +1 -1
  49. package/dist/db/repository/nodes.d.ts.map +1 -1
  50. package/dist/db/repository/nodes.js +8 -4
  51. package/dist/db/repository/nodes.js.map +1 -1
  52. package/dist/db/repository/sqlite-repository.d.ts +3 -0
  53. package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
  54. package/dist/db/repository/sqlite-repository.js +26 -0
  55. package/dist/db/repository/sqlite-repository.js.map +1 -1
  56. package/dist/domain/analysis/brief.d.ts.map +1 -1
  57. package/dist/domain/analysis/brief.js +13 -17
  58. package/dist/domain/analysis/brief.js.map +1 -1
  59. package/dist/domain/analysis/context.d.ts.map +1 -1
  60. package/dist/domain/analysis/context.js +14 -11
  61. package/dist/domain/analysis/context.js.map +1 -1
  62. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  63. package/dist/domain/analysis/dependencies.js +53 -52
  64. package/dist/domain/analysis/dependencies.js.map +1 -1
  65. package/dist/domain/analysis/fn-impact.d.ts +2 -7
  66. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  67. package/dist/domain/analysis/fn-impact.js +33 -31
  68. package/dist/domain/analysis/fn-impact.js.map +1 -1
  69. package/dist/domain/analysis/implementations.d.ts.map +1 -1
  70. package/dist/domain/analysis/implementations.js +11 -19
  71. package/dist/domain/analysis/implementations.js.map +1 -1
  72. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  73. package/dist/domain/analysis/module-map.js +55 -76
  74. package/dist/domain/analysis/module-map.js.map +1 -1
  75. package/dist/domain/analysis/query-helpers.d.ts +7 -0
  76. package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
  77. package/dist/domain/analysis/query-helpers.js +15 -1
  78. package/dist/domain/analysis/query-helpers.js.map +1 -1
  79. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  80. package/dist/domain/graph/builder/pipeline.js +315 -43
  81. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  82. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  83. package/dist/domain/graph/builder/stages/build-edges.js +106 -1
  84. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  85. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  86. package/dist/domain/graph/builder/stages/collect-files.js +17 -5
  87. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  88. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.js +99 -51
  90. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  91. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  92. package/dist/domain/graph/builder/stages/finalize.js +34 -7
  93. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  94. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  95. package/dist/domain/graph/builder/stages/insert-nodes.js +50 -26
  96. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  97. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  98. package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
  99. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  100. package/dist/domain/graph/cycles.d.ts +6 -0
  101. package/dist/domain/graph/cycles.d.ts.map +1 -1
  102. package/dist/domain/graph/cycles.js +114 -22
  103. package/dist/domain/graph/cycles.js.map +1 -1
  104. package/dist/domain/graph/resolve.js +1 -1
  105. package/dist/domain/graph/resolve.js.map +1 -1
  106. package/dist/domain/graph/watcher.d.ts +2 -0
  107. package/dist/domain/graph/watcher.d.ts.map +1 -1
  108. package/dist/domain/graph/watcher.js +170 -75
  109. package/dist/domain/graph/watcher.js.map +1 -1
  110. package/dist/domain/parser.d.ts +1 -6
  111. package/dist/domain/parser.d.ts.map +1 -1
  112. package/dist/domain/parser.js +101 -32
  113. package/dist/domain/parser.js.map +1 -1
  114. package/dist/domain/search/generator.js +1 -1
  115. package/dist/domain/search/generator.js.map +1 -1
  116. package/dist/domain/search/models.d.ts +4 -3
  117. package/dist/domain/search/models.d.ts.map +1 -1
  118. package/dist/domain/search/models.js +18 -5
  119. package/dist/domain/search/models.js.map +1 -1
  120. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  121. package/dist/domain/search/search/hybrid.js +29 -18
  122. package/dist/domain/search/search/hybrid.js.map +1 -1
  123. package/dist/extractors/clojure.d.ts +12 -0
  124. package/dist/extractors/clojure.d.ts.map +1 -0
  125. package/dist/extractors/clojure.js +245 -0
  126. package/dist/extractors/clojure.js.map +1 -0
  127. package/dist/extractors/cuda.d.ts +11 -0
  128. package/dist/extractors/cuda.d.ts.map +1 -0
  129. package/dist/extractors/cuda.js +302 -0
  130. package/dist/extractors/cuda.js.map +1 -0
  131. package/dist/extractors/erlang.d.ts +14 -0
  132. package/dist/extractors/erlang.d.ts.map +1 -0
  133. package/dist/extractors/erlang.js +239 -0
  134. package/dist/extractors/erlang.js.map +1 -0
  135. package/dist/extractors/fsharp.d.ts +13 -0
  136. package/dist/extractors/fsharp.d.ts.map +1 -0
  137. package/dist/extractors/fsharp.js +218 -0
  138. package/dist/extractors/fsharp.js.map +1 -0
  139. package/dist/extractors/gleam.d.ts +14 -0
  140. package/dist/extractors/gleam.d.ts.map +1 -0
  141. package/dist/extractors/gleam.js +229 -0
  142. package/dist/extractors/gleam.js.map +1 -0
  143. package/dist/extractors/go.js +36 -33
  144. package/dist/extractors/go.js.map +1 -1
  145. package/dist/extractors/groovy.d.ts +10 -0
  146. package/dist/extractors/groovy.d.ts.map +1 -0
  147. package/dist/extractors/groovy.js +304 -0
  148. package/dist/extractors/groovy.js.map +1 -0
  149. package/dist/extractors/helpers.d.ts.map +1 -1
  150. package/dist/extractors/helpers.js +40 -29
  151. package/dist/extractors/helpers.js.map +1 -1
  152. package/dist/extractors/index.d.ts +11 -0
  153. package/dist/extractors/index.d.ts.map +1 -1
  154. package/dist/extractors/index.js +11 -0
  155. package/dist/extractors/index.js.map +1 -1
  156. package/dist/extractors/java.js +58 -46
  157. package/dist/extractors/java.js.map +1 -1
  158. package/dist/extractors/javascript.js +46 -45
  159. package/dist/extractors/javascript.js.map +1 -1
  160. package/dist/extractors/julia.d.ts +16 -0
  161. package/dist/extractors/julia.d.ts.map +1 -0
  162. package/dist/extractors/julia.js +287 -0
  163. package/dist/extractors/julia.js.map +1 -0
  164. package/dist/extractors/kotlin.js +84 -78
  165. package/dist/extractors/kotlin.js.map +1 -1
  166. package/dist/extractors/objc.d.ts +9 -0
  167. package/dist/extractors/objc.d.ts.map +1 -0
  168. package/dist/extractors/objc.js +406 -0
  169. package/dist/extractors/objc.js.map +1 -0
  170. package/dist/extractors/ocaml.js +74 -0
  171. package/dist/extractors/ocaml.js.map +1 -1
  172. package/dist/extractors/python.js +29 -24
  173. package/dist/extractors/python.js.map +1 -1
  174. package/dist/extractors/r.d.ts +13 -0
  175. package/dist/extractors/r.d.ts.map +1 -0
  176. package/dist/extractors/r.js +251 -0
  177. package/dist/extractors/r.js.map +1 -0
  178. package/dist/extractors/rust.js +41 -32
  179. package/dist/extractors/rust.js.map +1 -1
  180. package/dist/extractors/solidity.d.ts +9 -0
  181. package/dist/extractors/solidity.d.ts.map +1 -0
  182. package/dist/extractors/solidity.js +365 -0
  183. package/dist/extractors/solidity.js.map +1 -0
  184. package/dist/extractors/swift.js +83 -81
  185. package/dist/extractors/swift.js.map +1 -1
  186. package/dist/extractors/verilog.d.ts +9 -0
  187. package/dist/extractors/verilog.d.ts.map +1 -0
  188. package/dist/extractors/verilog.js +286 -0
  189. package/dist/extractors/verilog.js.map +1 -0
  190. package/dist/extractors/zig.js +58 -60
  191. package/dist/extractors/zig.js.map +1 -1
  192. package/dist/features/ast.d.ts +16 -14
  193. package/dist/features/ast.d.ts.map +1 -1
  194. package/dist/features/ast.js +84 -83
  195. package/dist/features/ast.js.map +1 -1
  196. package/dist/features/audit.d.ts.map +1 -1
  197. package/dist/features/audit.js +8 -6
  198. package/dist/features/audit.js.map +1 -1
  199. package/dist/features/branch-compare.d.ts.map +1 -1
  200. package/dist/features/branch-compare.js +69 -72
  201. package/dist/features/branch-compare.js.map +1 -1
  202. package/dist/features/communities.d.ts.map +1 -1
  203. package/dist/features/communities.js +19 -7
  204. package/dist/features/communities.js.map +1 -1
  205. package/dist/features/complexity.d.ts.map +1 -1
  206. package/dist/features/complexity.js +120 -125
  207. package/dist/features/complexity.js.map +1 -1
  208. package/dist/features/dataflow.d.ts.map +1 -1
  209. package/dist/features/dataflow.js +136 -137
  210. package/dist/features/dataflow.js.map +1 -1
  211. package/dist/features/flow.d.ts.map +1 -1
  212. package/dist/features/flow.js +84 -79
  213. package/dist/features/flow.js.map +1 -1
  214. package/dist/features/structure-query.d.ts.map +1 -1
  215. package/dist/features/structure-query.js +69 -65
  216. package/dist/features/structure-query.js.map +1 -1
  217. package/dist/graph/algorithms/bfs.d.ts +2 -0
  218. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  219. package/dist/graph/algorithms/bfs.js +27 -0
  220. package/dist/graph/algorithms/bfs.js.map +1 -1
  221. package/dist/graph/algorithms/centrality.d.ts +2 -0
  222. package/dist/graph/algorithms/centrality.d.ts.map +1 -1
  223. package/dist/graph/algorithms/centrality.js +28 -0
  224. package/dist/graph/algorithms/centrality.js.map +1 -1
  225. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  226. package/dist/graph/algorithms/leiden/optimiser.js +70 -55
  227. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  228. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  229. package/dist/graph/algorithms/leiden/partition.js +288 -266
  230. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  231. package/dist/graph/algorithms/louvain.d.ts +3 -4
  232. package/dist/graph/algorithms/louvain.d.ts.map +1 -1
  233. package/dist/graph/algorithms/louvain.js +29 -0
  234. package/dist/graph/algorithms/louvain.js.map +1 -1
  235. package/dist/graph/algorithms/shortest-path.d.ts +2 -0
  236. package/dist/graph/algorithms/shortest-path.d.ts.map +1 -1
  237. package/dist/graph/algorithms/shortest-path.js +18 -1
  238. package/dist/graph/algorithms/shortest-path.js.map +1 -1
  239. package/dist/graph/model.d.ts.map +1 -1
  240. package/dist/graph/model.js +5 -1
  241. package/dist/graph/model.js.map +1 -1
  242. package/dist/infrastructure/config.d.ts.map +1 -1
  243. package/dist/infrastructure/config.js +6 -4
  244. package/dist/infrastructure/config.js.map +1 -1
  245. package/dist/infrastructure/suppress.d.ts +25 -0
  246. package/dist/infrastructure/suppress.d.ts.map +1 -0
  247. package/dist/infrastructure/suppress.js +43 -0
  248. package/dist/infrastructure/suppress.js.map +1 -0
  249. package/dist/mcp/server.d.ts.map +1 -1
  250. package/dist/mcp/server.js +29 -24
  251. package/dist/mcp/server.js.map +1 -1
  252. package/dist/presentation/dataflow.d.ts.map +1 -1
  253. package/dist/presentation/dataflow.js +47 -38
  254. package/dist/presentation/dataflow.js.map +1 -1
  255. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
  256. package/dist/presentation/diff-impact-mermaid.js +60 -51
  257. package/dist/presentation/diff-impact-mermaid.js.map +1 -1
  258. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  259. package/dist/presentation/queries-cli/exports.js +20 -14
  260. package/dist/presentation/queries-cli/exports.js.map +1 -1
  261. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  262. package/dist/presentation/queries-cli/impact.js +15 -13
  263. package/dist/presentation/queries-cli/impact.js.map +1 -1
  264. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  265. package/dist/presentation/queries-cli/inspect.js +101 -79
  266. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  267. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  268. package/dist/presentation/queries-cli/overview.js +25 -16
  269. package/dist/presentation/queries-cli/overview.js.map +1 -1
  270. package/dist/presentation/queries-cli/path.js +26 -20
  271. package/dist/presentation/queries-cli/path.js.map +1 -1
  272. package/dist/presentation/result-formatter.d.ts +10 -0
  273. package/dist/presentation/result-formatter.d.ts.map +1 -1
  274. package/dist/presentation/result-formatter.js +16 -1
  275. package/dist/presentation/result-formatter.js.map +1 -1
  276. package/dist/presentation/viewer.d.ts.map +1 -1
  277. package/dist/presentation/viewer.js +18 -12
  278. package/dist/presentation/viewer.js.map +1 -1
  279. package/dist/shared/errors.d.ts +5 -0
  280. package/dist/shared/errors.d.ts.map +1 -1
  281. package/dist/shared/errors.js +5 -0
  282. package/dist/shared/errors.js.map +1 -1
  283. package/dist/shared/hierarchy.d.ts +8 -2
  284. package/dist/shared/hierarchy.d.ts.map +1 -1
  285. package/dist/shared/hierarchy.js +42 -1
  286. package/dist/shared/hierarchy.js.map +1 -1
  287. package/dist/shared/normalize.d.ts +6 -1
  288. package/dist/shared/normalize.d.ts.map +1 -1
  289. package/dist/shared/normalize.js +20 -12
  290. package/dist/shared/normalize.js.map +1 -1
  291. package/dist/shared/paginate.d.ts +0 -9
  292. package/dist/shared/paginate.d.ts.map +1 -1
  293. package/dist/shared/paginate.js +0 -15
  294. package/dist/shared/paginate.js.map +1 -1
  295. package/dist/types.d.ts +129 -3
  296. package/dist/types.d.ts.map +1 -1
  297. package/grammars/tree-sitter-clojure.wasm +0 -0
  298. package/grammars/tree-sitter-cuda.wasm +0 -0
  299. package/grammars/tree-sitter-erlang.wasm +0 -0
  300. package/grammars/tree-sitter-fsharp.wasm +0 -0
  301. package/grammars/tree-sitter-gleam.wasm +0 -0
  302. package/grammars/tree-sitter-groovy.wasm +0 -0
  303. package/grammars/tree-sitter-julia.wasm +0 -0
  304. package/grammars/tree-sitter-objc.wasm +0 -0
  305. package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
  306. package/grammars/tree-sitter-r.wasm +0 -0
  307. package/grammars/tree-sitter-solidity.wasm +0 -0
  308. package/grammars/tree-sitter-verilog.wasm +0 -0
  309. package/package.json +18 -7
  310. package/src/ast-analysis/engine.ts +245 -42
  311. package/src/ast-analysis/metrics.ts +33 -11
  312. package/src/ast-analysis/rules/javascript.ts +0 -1
  313. package/src/ast-analysis/shared.ts +33 -24
  314. package/src/ast-analysis/visitor-utils.ts +52 -32
  315. package/src/ast-analysis/visitor.ts +132 -71
  316. package/src/ast-analysis/visitors/ast-store-visitor.ts +49 -119
  317. package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
  318. package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
  319. package/src/cli/commands/ast.ts +2 -2
  320. package/src/cli/commands/watch.ts +16 -2
  321. package/src/db/connection.ts +29 -28
  322. package/src/db/query-builder.ts +15 -3
  323. package/src/db/repository/base.ts +20 -0
  324. package/src/db/repository/native-repository.ts +79 -1
  325. package/src/db/repository/nodes.ts +13 -8
  326. package/src/db/repository/sqlite-repository.ts +29 -0
  327. package/src/domain/analysis/brief.ts +15 -25
  328. package/src/domain/analysis/context.ts +17 -10
  329. package/src/domain/analysis/dependencies.ts +67 -76
  330. package/src/domain/analysis/fn-impact.ts +36 -43
  331. package/src/domain/analysis/implementations.ts +11 -17
  332. package/src/domain/analysis/module-map.ts +58 -92
  333. package/src/domain/analysis/query-helpers.ts +18 -1
  334. package/src/domain/graph/builder/pipeline.ts +366 -41
  335. package/src/domain/graph/builder/stages/build-edges.ts +162 -1
  336. package/src/domain/graph/builder/stages/collect-files.ts +18 -7
  337. package/src/domain/graph/builder/stages/detect-changes.ts +110 -56
  338. package/src/domain/graph/builder/stages/finalize.ts +41 -11
  339. package/src/domain/graph/builder/stages/insert-nodes.ts +75 -39
  340. package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
  341. package/src/domain/graph/cycles.ts +110 -23
  342. package/src/domain/graph/resolve.ts +1 -1
  343. package/src/domain/graph/watcher.ts +202 -96
  344. package/src/domain/parser.ts +122 -28
  345. package/src/domain/search/generator.ts +1 -1
  346. package/src/domain/search/models.ts +17 -4
  347. package/src/domain/search/search/hybrid.ts +69 -51
  348. package/src/extractors/clojure.ts +273 -0
  349. package/src/extractors/cuda.ts +316 -0
  350. package/src/extractors/erlang.ts +252 -0
  351. package/src/extractors/fsharp.ts +253 -0
  352. package/src/extractors/gleam.ts +246 -0
  353. package/src/extractors/go.ts +43 -33
  354. package/src/extractors/groovy.ts +332 -0
  355. package/src/extractors/helpers.ts +37 -23
  356. package/src/extractors/index.ts +11 -0
  357. package/src/extractors/java.ts +66 -47
  358. package/src/extractors/javascript.ts +45 -46
  359. package/src/extractors/julia.ts +318 -0
  360. package/src/extractors/kotlin.ts +84 -77
  361. package/src/extractors/objc.ts +431 -0
  362. package/src/extractors/ocaml.ts +78 -0
  363. package/src/extractors/python.ts +31 -25
  364. package/src/extractors/r.ts +253 -0
  365. package/src/extractors/rust.ts +37 -29
  366. package/src/extractors/solidity.ts +394 -0
  367. package/src/extractors/swift.ts +81 -80
  368. package/src/extractors/verilog.ts +315 -0
  369. package/src/extractors/zig.ts +58 -61
  370. package/src/features/ast.ts +131 -112
  371. package/src/features/audit.ts +8 -6
  372. package/src/features/branch-compare.ts +105 -79
  373. package/src/features/communities.ts +25 -10
  374. package/src/features/complexity.ts +171 -134
  375. package/src/features/dataflow.ts +165 -175
  376. package/src/features/flow.ts +129 -92
  377. package/src/features/structure-query.ts +79 -64
  378. package/src/graph/algorithms/bfs.ts +34 -0
  379. package/src/graph/algorithms/centrality.ts +30 -0
  380. package/src/graph/algorithms/leiden/optimiser.ts +99 -55
  381. package/src/graph/algorithms/leiden/partition.ts +359 -294
  382. package/src/graph/algorithms/louvain.ts +31 -4
  383. package/src/graph/algorithms/shortest-path.ts +20 -1
  384. package/src/graph/model.ts +6 -1
  385. package/src/infrastructure/config.ts +6 -4
  386. package/src/infrastructure/suppress.ts +47 -0
  387. package/src/mcp/server.ts +53 -37
  388. package/src/presentation/dataflow.ts +50 -44
  389. package/src/presentation/diff-impact-mermaid.ts +104 -62
  390. package/src/presentation/queries-cli/exports.ts +21 -13
  391. package/src/presentation/queries-cli/impact.ts +15 -13
  392. package/src/presentation/queries-cli/inspect.ts +100 -81
  393. package/src/presentation/queries-cli/overview.ts +26 -16
  394. package/src/presentation/queries-cli/path.ts +33 -25
  395. package/src/presentation/result-formatter.ts +19 -1
  396. package/src/presentation/viewer.ts +42 -14
  397. package/src/shared/errors.ts +6 -0
  398. package/src/shared/hierarchy.ts +50 -2
  399. package/src/shared/normalize.ts +31 -12
  400. package/src/shared/paginate.ts +0 -17
  401. package/src/types.ts +138 -3
@@ -20,6 +20,127 @@ import type {
20
20
  WalkResults,
21
21
  } from '../types.js';
22
22
 
23
+ /** Merge each visitor's custom functionNodeTypes into the master set. */
24
+ function mergeFunctionNodeTypes(visitors: Visitor[], base: Set<string>): Set<string> {
25
+ const merged = new Set(base);
26
+ for (const v of visitors) {
27
+ if (v.functionNodeTypes) {
28
+ for (const t of v.functionNodeTypes) merged.add(t);
29
+ }
30
+ }
31
+ return merged;
32
+ }
33
+
34
+ /** Initialize all visitors for a given language. */
35
+ function initVisitors(visitors: Visitor[], langId: string): void {
36
+ for (const v of visitors) {
37
+ if (v.init) v.init(langId);
38
+ }
39
+ }
40
+
41
+ /** Check whether a visitor should be skipped at the current depth. */
42
+ function isSkipped(
43
+ skipDepths: Map<number, number>,
44
+ visitorIndex: number,
45
+ currentDepth: number,
46
+ ): boolean {
47
+ const skipAt = skipDepths.get(visitorIndex);
48
+ // Skipped if skip was requested at a shallower (or equal) depth
49
+ // We skip descendants, not the node itself, so skip when currentDepth > skipAt
50
+ return skipAt !== undefined && currentDepth > skipAt;
51
+ }
52
+
53
+ /** Dispatch enterFunction hooks to all non-skipped visitors. */
54
+ function dispatchEnterFunction(
55
+ visitors: Visitor[],
56
+ skipDepths: Map<number, number>,
57
+ node: TreeSitterNode,
58
+ funcName: string | null,
59
+ context: VisitorContext,
60
+ depth: number,
61
+ ): void {
62
+ for (let i = 0; i < visitors.length; i++) {
63
+ const v = visitors[i]!;
64
+ if (v.enterFunction && !isSkipped(skipDepths, i, depth)) {
65
+ v.enterFunction(node, funcName, context);
66
+ }
67
+ }
68
+ }
69
+
70
+ /** Dispatch enterNode hooks and track skipChildren requests. */
71
+ function dispatchEnterNode(
72
+ visitors: Visitor[],
73
+ skipDepths: Map<number, number>,
74
+ node: TreeSitterNode,
75
+ context: VisitorContext,
76
+ depth: number,
77
+ ): void {
78
+ for (let i = 0; i < visitors.length; i++) {
79
+ const v = visitors[i]!;
80
+ if (v.enterNode && !isSkipped(skipDepths, i, depth)) {
81
+ const result = v.enterNode(node, context);
82
+ if (result?.skipChildren) {
83
+ skipDepths.set(i, depth);
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ /** Dispatch exitNode hooks to all non-skipped visitors. */
90
+ function dispatchExitNode(
91
+ visitors: Visitor[],
92
+ skipDepths: Map<number, number>,
93
+ node: TreeSitterNode,
94
+ context: VisitorContext,
95
+ depth: number,
96
+ ): void {
97
+ for (let i = 0; i < visitors.length; i++) {
98
+ const v = visitors[i]!;
99
+ if (v.exitNode && !isSkipped(skipDepths, i, depth)) {
100
+ v.exitNode(node, context);
101
+ }
102
+ }
103
+ }
104
+
105
+ /** Clear skip flags for visitors that started skipping at this depth. */
106
+ function clearSkipFlags(
107
+ skipDepths: Map<number, number>,
108
+ visitorCount: number,
109
+ depth: number,
110
+ ): void {
111
+ for (let i = 0; i < visitorCount; i++) {
112
+ if (skipDepths.get(i) === depth) {
113
+ skipDepths.delete(i);
114
+ }
115
+ }
116
+ }
117
+
118
+ /** Dispatch exitFunction hooks to all non-skipped visitors. */
119
+ function dispatchExitFunction(
120
+ visitors: Visitor[],
121
+ skipDepths: Map<number, number>,
122
+ node: TreeSitterNode,
123
+ funcName: string | null,
124
+ context: VisitorContext,
125
+ depth: number,
126
+ ): void {
127
+ for (let i = 0; i < visitors.length; i++) {
128
+ const v = visitors[i]!;
129
+ if (v.exitFunction && !isSkipped(skipDepths, i, depth)) {
130
+ v.exitFunction(node, funcName, context);
131
+ }
132
+ }
133
+ }
134
+
135
+ /** Collect finish() results from all visitors into a name-keyed map. */
136
+ function collectResults(visitors: Visitor[]): WalkResults {
137
+ const results: WalkResults = {};
138
+ for (const v of visitors) {
139
+ results[v.name] = v.finish ? v.finish() : undefined;
140
+ }
141
+ return results;
142
+ }
143
+
23
144
  /**
24
145
  * Walk an AST root with multiple visitors in a single DFS pass.
25
146
  *
@@ -44,18 +165,8 @@ export function walkWithVisitors(
44
165
  getFunctionName = () => null,
45
166
  } = options;
46
167
 
47
- // Merge all visitors' functionNodeTypes into the master set
48
- const allFuncTypes = new Set(functionNodeTypes);
49
- for (const v of visitors) {
50
- if (v.functionNodeTypes) {
51
- for (const t of v.functionNodeTypes) allFuncTypes.add(t);
52
- }
53
- }
54
-
55
- // Initialize visitors
56
- for (const v of visitors) {
57
- if (v.init) v.init(langId);
58
- }
168
+ const allFuncTypes = mergeFunctionNodeTypes(visitors, functionNodeTypes);
169
+ initVisitors(visitors, langId);
59
170
 
60
171
  // Shared context object (mutated during walk)
61
172
  const scopeStack: ScopeEntry[] = [];
@@ -66,95 +177,45 @@ export function walkWithVisitors(
66
177
  scopeStack,
67
178
  };
68
179
 
69
- // Track which visitors have requested skipChildren at each depth
70
- // Key: visitor index, Value: depth at which skip was requested
71
180
  const skipDepths = new Map<number, number>();
72
181
 
73
182
  function walk(node: TreeSitterNode | null, depth: number): void {
74
183
  if (!node) return;
75
184
 
76
185
  const type = node.type;
77
- const isFunction = allFuncTypes.has(type);
186
+ const isFuncBoundary = allFuncTypes.has(type);
78
187
  let funcName: string | null = null;
79
188
 
80
- // Function boundary: enter
81
- if (isFunction) {
189
+ if (isFuncBoundary) {
82
190
  funcName = getFunctionName(node);
83
191
  context.currentFunction = node;
84
192
  scopeStack.push({ funcName, funcNode: node, params: new Map(), locals: new Map() });
85
- for (let i = 0; i < visitors.length; i++) {
86
- const v = visitors[i]!;
87
- if (v.enterFunction && !isSkipped(i, depth)) {
88
- v.enterFunction(node, funcName, context);
89
- }
90
- }
193
+ dispatchEnterFunction(visitors, skipDepths, node, funcName, context, depth);
91
194
  }
92
195
 
93
- // enterNode hooks
94
- for (let i = 0; i < visitors.length; i++) {
95
- const v = visitors[i]!;
96
- if (v.enterNode && !isSkipped(i, depth)) {
97
- const result = v.enterNode(node, context);
98
- if (result?.skipChildren) {
99
- skipDepths.set(i, depth);
100
- }
101
- }
102
- }
196
+ dispatchEnterNode(visitors, skipDepths, node, context, depth);
103
197
 
104
- // Nesting tracking
105
198
  const addsNesting = nestingNodeTypes.has(type);
106
199
  if (addsNesting) context.nestingLevel++;
107
200
 
108
- // Recurse children using node.child(i) (all children, not just named)
109
201
  for (let i = 0; i < node.childCount; i++) {
110
202
  walk(node.child(i), depth + 1);
111
203
  }
112
204
 
113
- // Undo nesting
114
205
  if (addsNesting) context.nestingLevel--;
115
206
 
116
- // exitNode hooks
117
- for (let i = 0; i < visitors.length; i++) {
118
- const v = visitors[i]!;
119
- if (v.exitNode && !isSkipped(i, depth)) {
120
- v.exitNode(node, context);
121
- }
122
- }
123
-
124
- // Clear skip for any visitor that started skipping at this depth
125
- for (let i = 0; i < visitors.length; i++) {
126
- if (skipDepths.get(i) === depth) {
127
- skipDepths.delete(i);
128
- }
129
- }
207
+ dispatchExitNode(visitors, skipDepths, node, context, depth);
208
+ clearSkipFlags(skipDepths, visitors.length, depth);
130
209
 
131
- // Function boundary: exit
132
- if (isFunction) {
133
- for (let i = 0; i < visitors.length; i++) {
134
- const v = visitors[i]!;
135
- if (v.exitFunction && !isSkipped(i, depth)) {
136
- v.exitFunction(node, funcName, context);
137
- }
138
- }
210
+ if (isFuncBoundary) {
211
+ dispatchExitFunction(visitors, skipDepths, node, funcName, context, depth);
139
212
  scopeStack.pop();
140
213
  context.currentFunction =
141
214
  scopeStack.length > 0 ? scopeStack[scopeStack.length - 1]!.funcNode : null;
142
215
  }
143
216
  }
144
217
 
145
- function isSkipped(visitorIndex: number, currentDepth: number): boolean {
146
- const skipAt = skipDepths.get(visitorIndex);
147
- // Skipped if skip was requested at a shallower (or equal) depth
148
- // We skip descendants, not the node itself, so skip when currentDepth > skipAt
149
- return skipAt !== undefined && currentDepth > skipAt;
150
- }
151
-
152
218
  walk(rootNode, 0);
153
219
 
154
- // Collect results
155
- const results: WalkResults = {};
156
- for (const v of visitors) {
157
- results[v.name] = v.finish ? v.finish() : undefined;
158
- }
159
- return results;
220
+ return collectResults(visitors);
160
221
  }
@@ -44,49 +44,33 @@ function extractExpressionText(node: TreeSitterNode): string | null {
44
44
  return truncate(node.text);
45
45
  }
46
46
 
47
- function extractCallName(node: TreeSitterNode): string {
48
- for (const field of ['function', 'method', 'name']) {
49
- const fn = node.childForFieldName(field);
50
- if (fn) return fn.text;
47
+ /** Extract the name from a throw statement's child nodes. */
48
+ function extractThrowName(node: TreeSitterNode): string | null {
49
+ for (let i = 0; i < node.childCount; i++) {
50
+ const child = node.child(i);
51
+ if (!child) continue;
52
+ if (child.type === 'new_expression') return extractNewName(child);
53
+ if (child.type === 'call_expression') {
54
+ const fn = child.childForFieldName('function');
55
+ return fn ? fn.text : child.text?.split('(')[0] || '?';
56
+ }
57
+ if (child.type === 'identifier') return child.text;
51
58
  }
52
- return node.text?.split('(')[0] || '?';
53
- }
54
-
55
- /** Extract receiver for call expressions (e.g. "obj" in "obj.method()"). */
56
- function extractCallReceiver(node: TreeSitterNode): string | null {
57
- const fn = node.childForFieldName('function');
58
- if (!fn || fn.type !== 'member_expression') return null;
59
- const obj = fn.childForFieldName('object');
60
- return obj ? obj.text : null;
59
+ return truncate(node.text);
61
60
  }
62
61
 
63
- function extractName(kind: string, node: TreeSitterNode): string | null {
64
- if (kind === 'throw') {
65
- for (let i = 0; i < node.childCount; i++) {
66
- const child = node.child(i);
67
- if (!child) continue;
68
- if (child.type === 'new_expression') return extractNewName(child);
69
- if (child.type === 'call_expression') {
70
- const fn = child.childForFieldName('function');
71
- return fn ? fn.text : child.text?.split('(')[0] || '?';
72
- }
73
- if (child.type === 'identifier') return child.text;
62
+ /** Extract the name from an await expression's child nodes. */
63
+ function extractAwaitName(node: TreeSitterNode): string | null {
64
+ for (let i = 0; i < node.childCount; i++) {
65
+ const child = node.child(i);
66
+ if (!child) continue;
67
+ if (child.type === 'call_expression') {
68
+ const fn = child.childForFieldName('function');
69
+ return fn ? fn.text : child.text?.split('(')[0] || '?';
74
70
  }
75
- return truncate(node.text);
76
- }
77
- if (kind === 'await') {
78
- for (let i = 0; i < node.childCount; i++) {
79
- const child = node.child(i);
80
- if (!child) continue;
81
- if (child.type === 'call_expression') {
82
- const fn = child.childForFieldName('function');
83
- return fn ? fn.text : child.text?.split('(')[0] || '?';
84
- }
85
- if (child.type === 'identifier' || child.type === 'member_expression') {
86
- return child.text;
87
- }
71
+ if (child.type === 'identifier' || child.type === 'member_expression') {
72
+ return child.text;
88
73
  }
89
- return truncate(node.text);
90
74
  }
91
75
  return truncate(node.text);
92
76
  }
@@ -118,90 +102,43 @@ export function createAstStoreVisitor(
118
102
  return nodeIdMap.get(`${parentDef.name}|${parentDef.kind}|${parentDef.line}`) || null;
119
103
  }
120
104
 
121
- /** Recursively walk a subtree collecting AST nodes — used for arguments-only traversal. */
122
- function walkSubtree(node: TreeSitterNode | null): void {
123
- if (!node) return;
124
- if (matched.has(node.id)) return;
125
-
126
- const kind = astTypeMap[node.type];
127
- if (kind === 'call') {
128
- // Capture this call and recurse only into its arguments
129
- collectNode(node, kind);
130
- walkCallArguments(node);
131
- return;
132
- }
133
- if (kind) {
134
- collectNode(node, kind);
135
- if (kind !== 'string' && kind !== 'regex') return; // skipChildren for non-leaf kinds
136
- }
137
- for (let i = 0; i < node.childCount; i++) {
138
- walkSubtree(node.child(i));
139
- }
140
- }
141
-
142
- /**
143
- * Recurse into only the arguments of a call node — mirrors the native engine's
144
- * strategy that prevents double-counting nested calls in the function field
145
- * (e.g. chained calls like `a().b()`).
146
- */
147
- function walkCallArguments(callNode: TreeSitterNode): void {
148
- // Try field-based lookup first, fall back to kind-based matching
149
- const argsNode =
150
- callNode.childForFieldName('arguments') ??
151
- findChildByKind(callNode, ['arguments', 'argument_list', 'method_arguments']);
152
- if (!argsNode) return;
153
- for (let i = 0; i < argsNode.childCount; i++) {
154
- walkSubtree(argsNode.child(i));
155
- }
156
- }
157
-
158
- function findChildByKind(node: TreeSitterNode, kinds: string[]): TreeSitterNode | null {
159
- for (let i = 0; i < node.childCount; i++) {
160
- const child = node.child(i);
161
- if (child && kinds.includes(child.type)) return child;
105
+ function resolveNameAndText(
106
+ node: TreeSitterNode,
107
+ kind: string,
108
+ ): { name: string | null | undefined; text: string | null; skip?: boolean } {
109
+ switch (kind) {
110
+ case 'new':
111
+ return { name: extractNewName(node), text: truncate(node.text) };
112
+ case 'throw':
113
+ return { name: extractThrowName(node), text: extractExpressionText(node) };
114
+ case 'await':
115
+ return { name: extractAwaitName(node), text: extractExpressionText(node) };
116
+ case 'string': {
117
+ const content = node.text?.replace(/^['"`]|['"`]$/g, '') || '';
118
+ if (content.length < 2) return { name: null, text: null, skip: true };
119
+ return { name: truncate(content, 100), text: truncate(node.text) };
120
+ }
121
+ case 'regex':
122
+ return { name: node.text || '?', text: truncate(node.text) };
123
+ default:
124
+ return { name: undefined, text: null };
162
125
  }
163
- return null;
164
126
  }
165
127
 
166
128
  function collectNode(node: TreeSitterNode, kind: string): void {
167
129
  if (matched.has(node.id)) return;
168
130
 
169
- const line = node.startPosition.row + 1;
170
- let name: string | null | undefined;
171
- let text: string | null = null;
172
- let receiver: string | null = null;
173
-
174
- if (kind === 'call') {
175
- name = extractCallName(node);
176
- text = truncate(node.text);
177
- receiver = extractCallReceiver(node);
178
- } else if (kind === 'new') {
179
- name = extractNewName(node);
180
- text = truncate(node.text);
181
- } else if (kind === 'throw') {
182
- name = extractName('throw', node);
183
- text = extractExpressionText(node);
184
- } else if (kind === 'await') {
185
- name = extractName('await', node);
186
- text = extractExpressionText(node);
187
- } else if (kind === 'string') {
188
- const content = node.text?.replace(/^['"`]|['"`]$/g, '') || '';
189
- if (content.length < 2) return;
190
- name = truncate(content, 100);
191
- text = truncate(node.text);
192
- } else if (kind === 'regex') {
193
- name = node.text || '?';
194
- text = truncate(node.text);
195
- }
131
+ const resolved = resolveNameAndText(node, kind);
132
+ if (resolved.skip) return;
196
133
 
197
134
  rows.push({
198
135
  file: relPath,
199
- line,
136
+ line: node.startPosition.row + 1,
200
137
  kind,
201
- name,
202
- text,
203
- receiver,
204
- parentNodeId: resolveParentNodeId(line),
138
+ name: resolved.name,
139
+ text: resolved.text,
140
+ receiver: null,
141
+ parentNodeId: resolveParentNodeId(node.startPosition.row + 1),
205
142
  });
206
143
 
207
144
  matched.add(node.id);
@@ -221,13 +158,6 @@ export function createAstStoreVisitor(
221
158
 
222
159
  collectNode(node, kind);
223
160
 
224
- if (kind === 'call') {
225
- // Mirror native: skip full subtree, recurse only into arguments.
226
- // Prevents double-counting chained calls like service.getUser().getName().
227
- walkCallArguments(node);
228
- return { skipChildren: true };
229
- }
230
-
231
161
  if (kind !== 'string' && kind !== 'regex') {
232
162
  return { skipChildren: true };
233
163
  }
@@ -156,6 +156,27 @@ function resetAccumulators(hRules: AnyRules | null | undefined): ComplexityAcc {
156
156
  };
157
157
  }
158
158
 
159
+ /** Classify a single node for all complexity metrics (Halstead, branching, logical ops, etc.). */
160
+ function classifyNode(
161
+ node: TreeSitterNode,
162
+ nestingLevel: number,
163
+ cRules: AnyRules,
164
+ hRules: AnyRules | null | undefined,
165
+ acc: ComplexityAcc,
166
+ ): void {
167
+ const type = node.type;
168
+
169
+ if (hRules) classifyHalstead(node, hRules, acc);
170
+ if (nestingLevel > acc.maxNesting) acc.maxNesting = nestingLevel;
171
+ if (type === cRules.logicalNodeType) classifyLogicalOp(node, cRules, acc);
172
+ if (type === cRules.optionalChainType) acc.cyclomatic++;
173
+ if (cRules.branchNodes.has(type) && node.childCount > 0) {
174
+ classifyBranchNode(node, type, nestingLevel, cRules, acc);
175
+ }
176
+ classifyPlainElse(node, type, cRules, acc);
177
+ if (cRules.caseNodes.has(type) && node.childCount > 0) acc.cyclomatic++;
178
+ }
179
+
159
180
  export function createComplexityVisitor(
160
181
  cRules: AnyRules,
161
182
  hRules?: AnyRules | null,
@@ -178,15 +199,11 @@ export function createComplexityVisitor(
178
199
  funcName: string | null,
179
200
  _context: VisitorContext,
180
201
  ): void {
181
- if (fileLevelWalk) {
182
- if (!activeFuncNode) {
183
- acc = resetAccumulators(hRules);
184
- activeFuncNode = funcNode;
185
- activeFuncName = funcName;
186
- funcDepth = 0;
187
- } else {
188
- funcDepth++;
189
- }
202
+ if (fileLevelWalk && !activeFuncNode) {
203
+ acc = resetAccumulators(hRules);
204
+ activeFuncNode = funcNode;
205
+ activeFuncName = funcName;
206
+ funcDepth = 0;
190
207
  } else {
191
208
  funcDepth++;
192
209
  }
@@ -197,18 +214,14 @@ export function createComplexityVisitor(
197
214
  _funcName: string | null,
198
215
  _context: VisitorContext,
199
216
  ): void {
200
- if (fileLevelWalk) {
201
- if (funcNode === activeFuncNode) {
202
- results.push({
203
- funcNode,
204
- funcName: activeFuncName,
205
- metrics: collectResult(funcNode, acc, hRules, langId),
206
- });
207
- activeFuncNode = null;
208
- activeFuncName = null;
209
- } else {
210
- funcDepth--;
211
- }
217
+ if (fileLevelWalk && funcNode === activeFuncNode) {
218
+ results.push({
219
+ funcNode,
220
+ funcName: activeFuncName,
221
+ metrics: collectResult(funcNode, acc, hRules, langId),
222
+ });
223
+ activeFuncNode = null;
224
+ activeFuncName = null;
212
225
  } else {
213
226
  funcDepth--;
214
227
  }
@@ -217,26 +230,8 @@ export function createComplexityVisitor(
217
230
  enterNode(node: TreeSitterNode, context: VisitorContext): EnterNodeResult | undefined {
218
231
  if (fileLevelWalk && !activeFuncNode) return;
219
232
 
220
- const type = node.type;
221
233
  const nestingLevel = fileLevelWalk ? context.nestingLevel + funcDepth : context.nestingLevel;
222
-
223
- if (hRules) classifyHalstead(node, hRules, acc);
224
-
225
- if (nestingLevel > acc.maxNesting) acc.maxNesting = nestingLevel;
226
-
227
- if (type === cRules.logicalNodeType) {
228
- classifyLogicalOp(node, cRules, acc);
229
- }
230
-
231
- if (type === cRules.optionalChainType) acc.cyclomatic++;
232
-
233
- if (cRules.branchNodes.has(type) && node.childCount > 0) {
234
- classifyBranchNode(node, type, nestingLevel, cRules, acc);
235
- }
236
-
237
- classifyPlainElse(node, type, cRules, acc);
238
-
239
- if (cRules.caseNodes.has(type) && node.childCount > 0) acc.cyclomatic++;
234
+ classifyNode(node, nestingLevel, cRules, hRules, acc);
240
235
  },
241
236
 
242
237
  exitNode(node: TreeSitterNode): void {