@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
@@ -1,15 +1,8 @@
1
- import {
2
- findDistinctCallers,
3
- findFileNodes,
4
- findImportDependents,
5
- findImportSources,
6
- findImportTargets,
7
- findNodesByFile,
8
- openReadonlyOrFail,
9
- } from '../../db/index.js';
1
+ import type { Repository } from '../../db/index.js';
10
2
  import { loadConfig } from '../../infrastructure/config.js';
11
3
  import { isTestFile } from '../../infrastructure/test-filter.js';
12
- import type { BetterSqlite3Database, ImportEdgeRow, NodeRow, RelatedNodeRow } from '../../types.js';
4
+ import type { ImportEdgeRow, NodeRow, RelatedNodeRow } from '../../types.js';
5
+ import { withRepo } from './query-helpers.js';
13
6
 
14
7
  /** Symbol kinds meaningful for a file brief — excludes parameters, properties, constants. */
15
8
  const BRIEF_KINDS = new Set([
@@ -49,7 +42,7 @@ function computeRiskTier(
49
42
  * Lightweight variant — only counts, does not collect details.
50
43
  */
51
44
  function countTransitiveCallers(
52
- db: BetterSqlite3Database,
45
+ repo: InstanceType<typeof Repository>,
53
46
  startId: number,
54
47
  noTests: boolean,
55
48
  maxDepth = 5,
@@ -60,7 +53,7 @@ function countTransitiveCallers(
60
53
  for (let d = 1; d <= maxDepth; d++) {
61
54
  const nextFrontier: number[] = [];
62
55
  for (const fid of frontier) {
63
- const callers = findDistinctCallers(db, fid) as RelatedNodeRow[];
56
+ const callers = repo.findDistinctCallers(fid) as RelatedNodeRow[];
64
57
  for (const c of callers) {
65
58
  if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
66
59
  visited.add(c.id);
@@ -80,7 +73,7 @@ function countTransitiveCallers(
80
73
  * Depth-bounded to match countTransitiveCallers and keep hook latency predictable.
81
74
  */
82
75
  function countTransitiveImporters(
83
- db: BetterSqlite3Database,
76
+ repo: InstanceType<typeof Repository>,
84
77
  fileNodeIds: number[],
85
78
  noTests: boolean,
86
79
  maxDepth = 5,
@@ -91,7 +84,7 @@ function countTransitiveImporters(
91
84
  for (let d = 1; d <= maxDepth; d++) {
92
85
  const nextFrontier: number[] = [];
93
86
  for (const current of frontier) {
94
- const dependents = findImportDependents(db, current) as RelatedNodeRow[];
87
+ const dependents = repo.findImportDependents(current) as RelatedNodeRow[];
95
88
  for (const dep of dependents) {
96
89
  if (!visited.has(dep.id) && (!noTests || !isTestFile(dep.file))) {
97
90
  visited.add(dep.id);
@@ -115,38 +108,37 @@ export function briefData(
115
108
  customDbPath: string,
116
109
  opts: { noTests?: boolean; config?: any } = {},
117
110
  ) {
118
- const db = openReadonlyOrFail(customDbPath);
119
- try {
111
+ return withRepo(customDbPath, (repo) => {
120
112
  const noTests = opts.noTests || false;
121
113
  const config = opts.config || loadConfig();
122
114
  const callerDepth = config.analysis?.briefCallerDepth ?? 5;
123
115
  const importerDepth = config.analysis?.briefImporterDepth ?? 5;
124
116
  const highRiskCallers = config.analysis?.briefHighRiskCallers ?? 10;
125
117
  const mediumRiskCallers = config.analysis?.briefMediumRiskCallers ?? 3;
126
- const fileNodes = findFileNodes(db, `%${file}%`) as NodeRow[];
118
+ const fileNodes = repo.findFileNodes(`%${file}%`) as NodeRow[];
127
119
  if (fileNodes.length === 0) {
128
120
  return { file, results: [] };
129
121
  }
130
122
 
131
123
  const results = fileNodes.map((fn) => {
132
124
  // Direct importers
133
- let importedBy = findImportSources(db, fn.id) as ImportEdgeRow[];
125
+ let importedBy = repo.findImportSources(fn.id) as ImportEdgeRow[];
134
126
  if (noTests) importedBy = importedBy.filter((i) => !isTestFile(i.file));
135
127
  const directImporters = [...new Set(importedBy.map((i) => i.file))];
136
128
 
137
129
  // Transitive importer count
138
- const totalImporterCount = countTransitiveImporters(db, [fn.id], noTests, importerDepth);
130
+ const totalImporterCount = countTransitiveImporters(repo, [fn.id], noTests, importerDepth);
139
131
 
140
132
  // Direct imports
141
- let importsTo = findImportTargets(db, fn.id) as ImportEdgeRow[];
133
+ let importsTo = repo.findImportTargets(fn.id) as ImportEdgeRow[];
142
134
  if (noTests) importsTo = importsTo.filter((i) => !isTestFile(i.file));
143
135
 
144
136
  // Symbol definitions with roles and caller counts
145
- const defs = (findNodesByFile(db, fn.file) as NodeRow[]).filter((d) =>
137
+ const defs = (repo.findNodesByFile(fn.file) as NodeRow[]).filter((d) =>
146
138
  BRIEF_KINDS.has(d.kind),
147
139
  );
148
140
  const symbols = defs.map((d) => {
149
- const callerCount = countTransitiveCallers(db, d.id, noTests, callerDepth);
141
+ const callerCount = countTransitiveCallers(repo, d.id, noTests, callerDepth);
150
142
  return {
151
143
  name: d.name,
152
144
  kind: d.kind,
@@ -169,7 +161,5 @@ export function briefData(
169
161
  });
170
162
 
171
163
  return { file, results };
172
- } finally {
173
- db.close();
174
- }
164
+ });
175
165
  }
@@ -264,6 +264,22 @@ function getNodeChildrenSafe(db: BetterSqlite3Database, nodeId: number) {
264
264
  }
265
265
  }
266
266
 
267
+ function buildIntraFileDataFlow(
268
+ db: BetterSqlite3Database,
269
+ file: string,
270
+ ): Array<{ caller: string; callees: string[] }> {
271
+ const intraEdges = findIntraFileCallEdges(db, file) as IntraFileCallEdge[];
272
+ const dataFlowMap = new Map<string, string[]>();
273
+ for (const edge of intraEdges) {
274
+ if (!dataFlowMap.has(edge.caller_name)) dataFlowMap.set(edge.caller_name, []);
275
+ dataFlowMap.get(edge.caller_name)!.push(edge.callee_name);
276
+ }
277
+ return [...dataFlowMap.entries()].map(([caller, callees]) => ({
278
+ caller,
279
+ callees,
280
+ }));
281
+ }
282
+
267
283
  function explainFileImpl(
268
284
  db: BetterSqlite3Database,
269
285
  target: string,
@@ -299,16 +315,7 @@ function explainFileImpl(
299
315
  file: r.file,
300
316
  }));
301
317
 
302
- const intraEdges = findIntraFileCallEdges(db, fn.file) as IntraFileCallEdge[];
303
- const dataFlowMap = new Map<string, string[]>();
304
- for (const edge of intraEdges) {
305
- if (!dataFlowMap.has(edge.caller_name)) dataFlowMap.set(edge.caller_name, []);
306
- dataFlowMap.get(edge.caller_name)!.push(edge.callee_name);
307
- }
308
- const dataFlow = [...dataFlowMap.entries()].map(([caller, callees]) => ({
309
- caller,
310
- callees,
311
- }));
318
+ const dataFlow = buildIntraFileDataFlow(db, fn.file);
312
319
 
313
320
  const metric = getLineCountForNode(db, fn.id) as { line_count: number } | undefined;
314
321
  let lineCount: number | null = metric?.line_count || null;
@@ -1,11 +1,4 @@
1
- import {
2
- findCallees,
3
- findCallers,
4
- findFileNodes,
5
- findImportSources,
6
- findImportTargets,
7
- findNodesByFile,
8
- } from '../../db/index.js';
1
+ import { findFileNodes, type Repository } from '../../db/index.js';
9
2
  import { cachedStmt } from '../../db/repository/cached-stmt.js';
10
3
  import { isTestFile } from '../../infrastructure/test-filter.js';
11
4
  import { resolveMethodViaHierarchy } from '../../shared/hierarchy.js';
@@ -18,13 +11,11 @@ import type {
18
11
  RelatedNodeRow,
19
12
  StmtCache,
20
13
  } from '../../types.js';
21
- import { withReadonlyDb } from './query-helpers.js';
14
+ import { withReadonlyDb, withRepo } from './query-helpers.js';
22
15
  import { findMatchingNodes } from './symbol-lookup.js';
23
16
 
24
- type UpstreamRow = { id: number; name: string; kind: string; file: string; line: number };
25
17
  type NodeByIdRow = { name: string; kind: string; file: string; line: number };
26
18
 
27
- const _upstreamStmtCache: StmtCache<UpstreamRow> = new WeakMap();
28
19
  const _nodeByIdStmtCache: StmtCache<NodeByIdRow> = new WeakMap();
29
20
 
30
21
  export function fileDepsData(
@@ -32,21 +23,21 @@ export function fileDepsData(
32
23
  customDbPath: string,
33
24
  opts: { noTests?: boolean; limit?: number; offset?: number } = {},
34
25
  ) {
35
- return withReadonlyDb(customDbPath, (db) => {
26
+ return withRepo(customDbPath, (repo) => {
36
27
  const noTests = opts.noTests || false;
37
- const fileNodes = findFileNodes(db, `%${file}%`) as NodeRow[];
28
+ const fileNodes = repo.findFileNodes(`%${file}%`) as NodeRow[];
38
29
  if (fileNodes.length === 0) {
39
30
  return { file, results: [] };
40
31
  }
41
32
 
42
33
  const results = fileNodes.map((fn) => {
43
- let importsTo = findImportTargets(db, fn.id) as ImportEdgeRow[];
34
+ let importsTo = repo.findImportTargets(fn.id) as ImportEdgeRow[];
44
35
  if (noTests) importsTo = importsTo.filter((i) => !isTestFile(i.file));
45
36
 
46
- let importedBy = findImportSources(db, fn.id) as ImportEdgeRow[];
37
+ let importedBy = repo.findImportSources(fn.id) as ImportEdgeRow[];
47
38
  if (noTests) importedBy = importedBy.filter((i) => !isTestFile(i.file));
48
39
 
49
- const defs = findNodesByFile(db, fn.file) as NodeRow[];
40
+ const defs = repo.findNodesByFile(fn.file) as NodeRow[];
50
41
 
51
42
  return {
52
43
  file: fn.file,
@@ -64,9 +55,11 @@ export function fileDepsData(
64
55
  /**
65
56
  * BFS transitive caller traversal starting from `callers` of `nodeId`.
66
57
  * Returns an object keyed by depth (2..depth) -> array of caller descriptors.
58
+ *
59
+ * Uses Repository.findCallers() so it works with both native and WASM engines.
67
60
  */
68
61
  function buildTransitiveCallers(
69
- db: BetterSqlite3Database,
62
+ repo: InstanceType<typeof Repository>,
70
63
  callers: Array<{ id: number; name: string; kind: string; file: string; line: number }>,
71
64
  nodeId: number,
72
65
  depth: number,
@@ -81,28 +74,12 @@ function buildTransitiveCallers(
81
74
  const visited = new Set([nodeId]);
82
75
  let frontier = callers;
83
76
 
84
- const upstreamStmt = cachedStmt(
85
- _upstreamStmtCache,
86
- db,
87
- `
88
- SELECT n.id, n.name, n.kind, n.file, n.line
89
- FROM edges e JOIN nodes n ON e.source_id = n.id
90
- WHERE e.target_id = ? AND e.kind = 'calls'
91
- `,
92
- );
93
-
94
77
  for (let d = 2; d <= depth; d++) {
95
78
  const nextFrontier: typeof frontier = [];
96
79
  for (const f of frontier) {
97
80
  if (visited.has(f.id)) continue;
98
81
  visited.add(f.id);
99
- const upstream = upstreamStmt.all(f.id) as Array<{
100
- id: number;
101
- name: string;
102
- kind: string;
103
- file: string;
104
- line: number;
105
- }>;
82
+ const upstream = repo.findCallers(f.id) as RelatedNodeRow[];
106
83
  for (const u of upstream) {
107
84
  if (noTests && isTestFile(u.file)) continue;
108
85
  if (!visited.has(u.id)) {
@@ -125,6 +102,59 @@ function buildTransitiveCallers(
125
102
  return transitiveCallers;
126
103
  }
127
104
 
105
+ function collectCallersWithHierarchy(
106
+ repo: InstanceType<typeof Repository>,
107
+ node: NodeRow,
108
+ noTests: boolean,
109
+ ): Array<RelatedNodeRow & { viaHierarchy?: string }> {
110
+ let callers: Array<RelatedNodeRow & { viaHierarchy?: string }> = repo.findCallers(
111
+ node.id,
112
+ ) as RelatedNodeRow[];
113
+
114
+ if (node.kind === 'method' && node.name.includes('.')) {
115
+ const methodName = node.name.split('.').pop()!;
116
+ const relatedMethods = resolveMethodViaHierarchy(repo, methodName);
117
+ for (const rm of relatedMethods) {
118
+ if (rm.id === node.id) continue;
119
+ const extraCallers = repo.findCallers(rm.id) as RelatedNodeRow[];
120
+ callers.push(...extraCallers.map((c) => ({ ...c, viaHierarchy: rm.name })));
121
+ }
122
+ }
123
+ if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
124
+ return callers;
125
+ }
126
+
127
+ function buildNodeDepsResult(
128
+ repo: InstanceType<typeof Repository>,
129
+ node: NodeRow,
130
+ hc: Map<string, string | null>,
131
+ depth: number,
132
+ noTests: boolean,
133
+ ) {
134
+ const callees = repo.findCallees(node.id) as RelatedNodeRow[];
135
+ const filteredCallees = noTests ? callees.filter((c) => !isTestFile(c.file)) : callees;
136
+ const callers = collectCallersWithHierarchy(repo, node, noTests);
137
+ const transitiveCallers = buildTransitiveCallers(repo, callers, node.id, depth, noTests);
138
+
139
+ return {
140
+ ...normalizeSymbol(node, repo, hc),
141
+ callees: filteredCallees.map((c) => ({
142
+ name: c.name,
143
+ kind: c.kind,
144
+ file: c.file,
145
+ line: c.line,
146
+ })),
147
+ callers: callers.map((c) => ({
148
+ name: c.name,
149
+ kind: c.kind,
150
+ file: c.file,
151
+ line: c.line,
152
+ viaHierarchy: c.viaHierarchy || undefined,
153
+ })),
154
+ transitiveCallers,
155
+ };
156
+ }
157
+
128
158
  export function fnDepsData(
129
159
  name: string,
130
160
  customDbPath: string,
@@ -137,56 +167,17 @@ export function fnDepsData(
137
167
  offset?: number;
138
168
  } = {},
139
169
  ) {
140
- return withReadonlyDb(customDbPath, (db) => {
170
+ return withRepo(customDbPath, (repo) => {
141
171
  const depth = opts.depth || 3;
142
172
  const noTests = opts.noTests || false;
143
173
  const hc = new Map();
144
174
 
145
- const nodes = findMatchingNodes(db, name, { noTests, file: opts.file, kind: opts.kind });
175
+ const nodes = findMatchingNodes(repo, name, { noTests, file: opts.file, kind: opts.kind });
146
176
  if (nodes.length === 0) {
147
177
  return { name, results: [] };
148
178
  }
149
179
 
150
- const results = nodes.map((node) => {
151
- const callees = findCallees(db, node.id) as RelatedNodeRow[];
152
- const filteredCallees = noTests ? callees.filter((c) => !isTestFile(c.file)) : callees;
153
-
154
- let callers: Array<RelatedNodeRow & { viaHierarchy?: string }> = findCallers(
155
- db,
156
- node.id,
157
- ) as RelatedNodeRow[];
158
-
159
- if (node.kind === 'method' && node.name.includes('.')) {
160
- const methodName = node.name.split('.').pop()!;
161
- const relatedMethods = resolveMethodViaHierarchy(db, methodName);
162
- for (const rm of relatedMethods) {
163
- if (rm.id === node.id) continue;
164
- const extraCallers = findCallers(db, rm.id) as RelatedNodeRow[];
165
- callers.push(...extraCallers.map((c) => ({ ...c, viaHierarchy: rm.name })));
166
- }
167
- }
168
- if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
169
-
170
- const transitiveCallers = buildTransitiveCallers(db, callers, node.id, depth, noTests);
171
-
172
- return {
173
- ...normalizeSymbol(node, db, hc),
174
- callees: filteredCallees.map((c) => ({
175
- name: c.name,
176
- kind: c.kind,
177
- file: c.file,
178
- line: c.line,
179
- })),
180
- callers: callers.map((c) => ({
181
- name: c.name,
182
- kind: c.kind,
183
- file: c.file,
184
- line: c.line,
185
- viaHierarchy: c.viaHierarchy || undefined,
186
- })),
187
- transitiveCallers,
188
- };
189
- });
180
+ const results = nodes.map((node) => buildNodeDepsResult(repo, node, hc, depth, noTests));
190
181
 
191
182
  const base = { name, results };
192
183
  return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
@@ -1,40 +1,32 @@
1
- import {
2
- findDistinctCallers,
3
- findFileNodes,
4
- findImplementors,
5
- findImportDependents,
6
- findNodeById,
7
- } from '../../db/index.js';
1
+ import { Repository, SqliteRepository } from '../../db/index.js';
8
2
  import { isTestFile } from '../../infrastructure/test-filter.js';
9
3
  import { normalizeSymbol } from '../../shared/normalize.js';
10
4
  import { paginateResult } from '../../shared/paginate.js';
11
5
  import type { BetterSqlite3Database, NodeRow, RelatedNodeRow } from '../../types.js';
12
- import { resolveAnalysisOpts, withReadonlyDb } from './query-helpers.js';
6
+ import { resolveAnalysisOpts, withRepo } from './query-helpers.js';
13
7
  import { findMatchingNodes } from './symbol-lookup.js';
14
8
 
9
+ /** Cache so repeated raw-db calls reuse the same SqliteRepository (preserves per-instance memoization). */
10
+ const repoCache = new WeakMap<BetterSqlite3Database, InstanceType<typeof SqliteRepository>>();
11
+
12
+ /** Coerce a raw db handle or Repository into a Repository instance. */
13
+ function toRepo(
14
+ dbOrRepo: BetterSqlite3Database | InstanceType<typeof Repository>,
15
+ ): InstanceType<typeof Repository> {
16
+ if (dbOrRepo instanceof Repository) return dbOrRepo;
17
+ const db = dbOrRepo as BetterSqlite3Database;
18
+ let repo = repoCache.get(db);
19
+ if (!repo) {
20
+ repo = new SqliteRepository(db);
21
+ repoCache.set(db, repo);
22
+ }
23
+ return repo;
24
+ }
25
+
15
26
  // --- Shared BFS: transitive callers ---
16
27
 
17
28
  const INTERFACE_LIKE_KINDS = new Set(['interface', 'trait']);
18
29
 
19
- /**
20
- * Check whether the graph contains any 'implements' edges.
21
- * Cached per db handle so the query runs at most once per connection.
22
- */
23
- const _hasImplementsCache: WeakMap<BetterSqlite3Database, boolean> = new WeakMap();
24
- function hasImplementsEdges(db: BetterSqlite3Database): boolean {
25
- if (_hasImplementsCache.has(db)) return _hasImplementsCache.get(db)!;
26
- const row = db.prepare("SELECT 1 FROM edges WHERE kind = 'implements' LIMIT 1").get();
27
- const result = !!row;
28
- _hasImplementsCache.set(db, result);
29
- return result;
30
- }
31
-
32
- /**
33
- * BFS traversal to find transitive callers of a node.
34
- * When an interface/trait node is encountered (either as the start node or
35
- * during traversal), its concrete implementors are also added to the frontier
36
- * so that changes to an interface signature propagate to all implementors.
37
- */
38
30
  type BfsLevel = Array<{
39
31
  name: string;
40
32
  kind: string;
@@ -76,7 +68,7 @@ function recordImplementor(
76
68
 
77
69
  /** Expand implementors for an interface/trait node into the BFS frontier. */
78
70
  function expandImplementors(
79
- db: BetterSqlite3Database,
71
+ repo: InstanceType<typeof Repository>,
80
72
  nodeId: number,
81
73
  depth: number,
82
74
  visited: Set<number>,
@@ -85,14 +77,14 @@ function expandImplementors(
85
77
  noTests: boolean,
86
78
  onVisit?: BfsOnVisit,
87
79
  ): void {
88
- const impls = findImplementors(db, nodeId) as RelatedNodeRow[];
80
+ const impls = repo.findImplementors(nodeId) as RelatedNodeRow[];
89
81
  for (const impl of impls) {
90
82
  recordImplementor(impl, nodeId, depth, visited, frontier, levels, noTests, onVisit);
91
83
  }
92
84
  }
93
85
 
94
86
  export function bfsTransitiveCallers(
95
- db: BetterSqlite3Database,
87
+ dbOrRepo: BetterSqlite3Database | InstanceType<typeof Repository>,
96
88
  startId: number,
97
89
  {
98
90
  noTests = false,
@@ -106,7 +98,8 @@ export function bfsTransitiveCallers(
106
98
  onVisit?: BfsOnVisit;
107
99
  } = {},
108
100
  ) {
109
- const resolveImplementors = includeImplementors && hasImplementsEdges(db);
101
+ const repo = toRepo(dbOrRepo);
102
+ const resolveImplementors = includeImplementors && repo.hasImplementsEdges();
110
103
  const visited = new Set([startId]);
111
104
  const levels: BfsLevels = {};
112
105
  let frontier = [startId];
@@ -114,9 +107,9 @@ export function bfsTransitiveCallers(
114
107
  // Seed: if start node is an interface/trait, include its implementors at depth 1
115
108
  const implNextFrontier: number[] = [];
116
109
  if (resolveImplementors) {
117
- const startNode = findNodeById(db, startId) as NodeRow | undefined;
110
+ const startNode = repo.findNodeById(startId) as NodeRow | undefined;
118
111
  if (startNode && INTERFACE_LIKE_KINDS.has(startNode.kind)) {
119
- expandImplementors(db, startId, 1, visited, implNextFrontier, levels, noTests, onVisit);
112
+ expandImplementors(repo, startId, 1, visited, implNextFrontier, levels, noTests, onVisit);
120
113
  }
121
114
  }
122
115
 
@@ -126,7 +119,7 @@ export function bfsTransitiveCallers(
126
119
  }
127
120
  const nextFrontier: number[] = [];
128
121
  for (const fid of frontier) {
129
- const callers = findDistinctCallers(db, fid) as RelatedNodeRow[];
122
+ const callers = repo.findDistinctCallers(fid) as RelatedNodeRow[];
130
123
  for (const c of callers) {
131
124
  if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
132
125
  visited.add(c.id);
@@ -136,7 +129,7 @@ export function bfsTransitiveCallers(
136
129
  if (onVisit) onVisit(c, fid, d);
137
130
  }
138
131
  if (resolveImplementors && INTERFACE_LIKE_KINDS.has(c.kind)) {
139
- expandImplementors(db, c.id, d + 1, visited, nextFrontier, levels, noTests, onVisit);
132
+ expandImplementors(repo, c.id, d + 1, visited, nextFrontier, levels, noTests, onVisit);
140
133
  }
141
134
  }
142
135
  }
@@ -152,9 +145,9 @@ export function impactAnalysisData(
152
145
  customDbPath: string,
153
146
  opts: { noTests?: boolean } = {},
154
147
  ) {
155
- return withReadonlyDb(customDbPath, (db) => {
148
+ return withRepo(customDbPath, (repo) => {
156
149
  const noTests = opts.noTests || false;
157
- const fileNodes = findFileNodes(db, `%${file}%`) as NodeRow[];
150
+ const fileNodes = repo.findFileNodes(`%${file}%`) as NodeRow[];
158
151
  if (fileNodes.length === 0) {
159
152
  return { file, sources: [], levels: {}, totalDependents: 0 };
160
153
  }
@@ -172,7 +165,7 @@ export function impactAnalysisData(
172
165
  while (queue.length > 0) {
173
166
  const current = queue.shift()!;
174
167
  const level = levels.get(current)!;
175
- const dependents = findImportDependents(db, current) as RelatedNodeRow[];
168
+ const dependents = repo.findImportDependents(current) as RelatedNodeRow[];
176
169
  for (const dep of dependents) {
177
170
  if (!visited.has(dep.id) && (!noTests || !isTestFile(dep.file))) {
178
171
  visited.add(dep.id);
@@ -186,7 +179,7 @@ export function impactAnalysisData(
186
179
  for (const [id, level] of levels) {
187
180
  if (level === 0) continue;
188
181
  if (!byLevel[level]) byLevel[level] = [];
189
- const node = findNodeById(db, id) as NodeRow | undefined;
182
+ const node = repo.findNodeById(id) as NodeRow | undefined;
190
183
  if (node) byLevel[level].push({ file: node.file });
191
184
  }
192
185
 
@@ -213,12 +206,12 @@ export function fnImpactData(
213
206
  config?: any;
214
207
  } = {},
215
208
  ) {
216
- return withReadonlyDb(customDbPath, (db) => {
209
+ return withRepo(customDbPath, (repo) => {
217
210
  const { noTests, config } = resolveAnalysisOpts(opts);
218
211
  const maxDepth = opts.depth || config.analysis?.fnImpactDepth || 5;
219
212
  const hc = new Map();
220
213
 
221
- const nodes = findMatchingNodes(db, name, { noTests, file: opts.file, kind: opts.kind });
214
+ const nodes = findMatchingNodes(repo, name, { noTests, file: opts.file, kind: opts.kind });
222
215
  if (nodes.length === 0) {
223
216
  return { name, results: [] };
224
217
  }
@@ -226,13 +219,13 @@ export function fnImpactData(
226
219
  const includeImplementors = opts.includeImplementors !== false;
227
220
 
228
221
  const results = nodes.map((node) => {
229
- const { levels, totalDependents } = bfsTransitiveCallers(db, node.id, {
222
+ const { levels, totalDependents } = bfsTransitiveCallers(repo, node.id, {
230
223
  noTests,
231
224
  maxDepth,
232
225
  includeImplementors,
233
226
  });
234
227
  return {
235
- ...normalizeSymbol(node, db, hc),
228
+ ...normalizeSymbol(node, repo, hc),
236
229
  levels,
237
230
  totalDependents,
238
231
  };
@@ -1,9 +1,9 @@
1
- import { findImplementors, findInterfaces, openReadonlyOrFail } from '../../db/index.js';
2
1
  import { isTestFile } from '../../infrastructure/test-filter.js';
3
2
  import { CORE_SYMBOL_KINDS } from '../../shared/kinds.js';
4
3
  import { normalizeSymbol } from '../../shared/normalize.js';
5
4
  import { paginateResult } from '../../shared/paginate.js';
6
5
  import type { RelatedNodeRow } from '../../types.js';
6
+ import { withRepo } from './query-helpers.js';
7
7
  import { findMatchingNodes } from './symbol-lookup.js';
8
8
 
9
9
  /**
@@ -14,12 +14,11 @@ export function implementationsData(
14
14
  customDbPath: string,
15
15
  opts: { noTests?: boolean; file?: string; kind?: string; limit?: number; offset?: number } = {},
16
16
  ) {
17
- const db = openReadonlyOrFail(customDbPath);
18
- try {
17
+ return withRepo(customDbPath, (repo) => {
19
18
  const noTests = opts.noTests || false;
20
19
  const hc = new Map();
21
20
 
22
- const nodes = findMatchingNodes(db, name, {
21
+ const nodes = findMatchingNodes(repo, name, {
23
22
  noTests,
24
23
  file: opts.file,
25
24
  kind: opts.kind,
@@ -30,11 +29,11 @@ export function implementationsData(
30
29
  }
31
30
 
32
31
  const results = nodes.map((node) => {
33
- let implementors = findImplementors(db, node.id) as RelatedNodeRow[];
32
+ let implementors = repo.findImplementors(node.id) as RelatedNodeRow[];
34
33
  if (noTests) implementors = implementors.filter((n) => !isTestFile(n.file));
35
34
 
36
35
  return {
37
- ...normalizeSymbol(node, db, hc),
36
+ ...normalizeSymbol(node, repo, hc),
38
37
  implementors: implementors.map((impl) => ({
39
38
  name: impl.name,
40
39
  kind: impl.kind,
@@ -46,9 +45,7 @@ export function implementationsData(
46
45
 
47
46
  const base = { name, results };
48
47
  return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
49
- } finally {
50
- db.close();
51
- }
48
+ });
52
49
  }
53
50
 
54
51
  /**
@@ -59,12 +56,11 @@ export function interfacesData(
59
56
  customDbPath: string,
60
57
  opts: { noTests?: boolean; file?: string; kind?: string; limit?: number; offset?: number } = {},
61
58
  ) {
62
- const db = openReadonlyOrFail(customDbPath);
63
- try {
59
+ return withRepo(customDbPath, (repo) => {
64
60
  const noTests = opts.noTests || false;
65
61
  const hc = new Map();
66
62
 
67
- const nodes = findMatchingNodes(db, name, {
63
+ const nodes = findMatchingNodes(repo, name, {
68
64
  noTests,
69
65
  file: opts.file,
70
66
  kind: opts.kind,
@@ -75,11 +71,11 @@ export function interfacesData(
75
71
  }
76
72
 
77
73
  const results = nodes.map((node) => {
78
- let interfaces = findInterfaces(db, node.id) as RelatedNodeRow[];
74
+ let interfaces = repo.findInterfaces(node.id) as RelatedNodeRow[];
79
75
  if (noTests) interfaces = interfaces.filter((n) => !isTestFile(n.file));
80
76
 
81
77
  return {
82
- ...normalizeSymbol(node, db, hc),
78
+ ...normalizeSymbol(node, repo, hc),
83
79
  interfaces: interfaces.map((iface) => ({
84
80
  name: iface.name,
85
81
  kind: iface.kind,
@@ -91,7 +87,5 @@ export function interfacesData(
91
87
 
92
88
  const base = { name, results };
93
89
  return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
94
- } finally {
95
- db.close();
96
- }
90
+ });
97
91
  }