@optave/codegraph 3.4.1 → 3.6.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 (349) hide show
  1. package/README.md +50 -28
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +119 -127
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/javascript.js +1 -0
  7. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  8. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/ast-store-visitor.js +116 -35
  10. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  11. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  12. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  13. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  14. package/dist/db/better-sqlite3.d.ts +3 -0
  15. package/dist/db/better-sqlite3.d.ts.map +1 -0
  16. package/dist/db/better-sqlite3.js +19 -0
  17. package/dist/db/better-sqlite3.js.map +1 -0
  18. package/dist/db/connection.d.ts +25 -4
  19. package/dist/db/connection.d.ts.map +1 -1
  20. package/dist/db/connection.js +125 -23
  21. package/dist/db/connection.js.map +1 -1
  22. package/dist/db/index.d.ts +2 -2
  23. package/dist/db/index.d.ts.map +1 -1
  24. package/dist/db/index.js +1 -1
  25. package/dist/db/index.js.map +1 -1
  26. package/dist/db/migrations.d.ts.map +1 -1
  27. package/dist/db/migrations.js +40 -32
  28. package/dist/db/migrations.js.map +1 -1
  29. package/dist/db/query-builder.d.ts +5 -5
  30. package/dist/db/query-builder.d.ts.map +1 -1
  31. package/dist/db/query-builder.js +20 -4
  32. package/dist/db/query-builder.js.map +1 -1
  33. package/dist/db/repository/index.d.ts +1 -0
  34. package/dist/db/repository/index.d.ts.map +1 -1
  35. package/dist/db/repository/index.js +1 -0
  36. package/dist/db/repository/index.js.map +1 -1
  37. package/dist/db/repository/native-repository.d.ts +58 -0
  38. package/dist/db/repository/native-repository.d.ts.map +1 -0
  39. package/dist/db/repository/native-repository.js +261 -0
  40. package/dist/db/repository/native-repository.js.map +1 -0
  41. package/dist/db/repository/nodes.d.ts +4 -4
  42. package/dist/db/repository/nodes.d.ts.map +1 -1
  43. package/dist/db/repository/nodes.js +6 -6
  44. package/dist/db/repository/nodes.js.map +1 -1
  45. package/dist/domain/analysis/context.d.ts.map +1 -1
  46. package/dist/domain/analysis/context.js +51 -66
  47. package/dist/domain/analysis/context.js.map +1 -1
  48. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  49. package/dist/domain/analysis/dependencies.js +62 -70
  50. package/dist/domain/analysis/dependencies.js.map +1 -1
  51. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  52. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  53. package/dist/domain/analysis/exports.d.ts.map +1 -1
  54. package/dist/domain/analysis/exports.js +29 -33
  55. package/dist/domain/analysis/exports.js.map +1 -1
  56. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  57. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  58. package/dist/domain/analysis/fn-impact.js +35 -65
  59. package/dist/domain/analysis/fn-impact.js.map +1 -1
  60. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  61. package/dist/domain/analysis/module-map.js +91 -6
  62. package/dist/domain/analysis/module-map.js.map +1 -1
  63. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  64. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  65. package/dist/domain/analysis/query-helpers.js +27 -0
  66. package/dist/domain/analysis/query-helpers.js.map +1 -0
  67. package/dist/domain/graph/builder/context.d.ts +2 -1
  68. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  69. package/dist/domain/graph/builder/context.js +1 -0
  70. package/dist/domain/graph/builder/context.js.map +1 -1
  71. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  72. package/dist/domain/graph/builder/helpers.js +15 -9
  73. package/dist/domain/graph/builder/helpers.js.map +1 -1
  74. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  75. package/dist/domain/graph/builder/incremental.js +3 -2
  76. package/dist/domain/graph/builder/incremental.js.map +1 -1
  77. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  78. package/dist/domain/graph/builder/pipeline.js +95 -7
  79. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  80. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  81. package/dist/domain/graph/builder/stages/build-edges.js +101 -57
  82. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  83. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  84. package/dist/domain/graph/builder/stages/build-structure.js +33 -3
  85. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  86. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  87. package/dist/domain/graph/builder/stages/collect-files.js +70 -6
  88. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  90. package/dist/domain/graph/builder/stages/detect-changes.js +36 -14
  91. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  92. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  93. package/dist/domain/graph/builder/stages/finalize.js +130 -88
  94. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  95. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  96. package/dist/domain/graph/builder/stages/insert-nodes.js +124 -16
  97. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  98. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  99. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  100. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  101. package/dist/domain/graph/resolve.d.ts +0 -4
  102. package/dist/domain/graph/resolve.d.ts.map +1 -1
  103. package/dist/domain/graph/resolve.js +32 -48
  104. package/dist/domain/graph/resolve.js.map +1 -1
  105. package/dist/domain/graph/watcher.d.ts.map +1 -1
  106. package/dist/domain/graph/watcher.js +12 -12
  107. package/dist/domain/graph/watcher.js.map +1 -1
  108. package/dist/domain/parser.d.ts +1 -1
  109. package/dist/domain/parser.d.ts.map +1 -1
  110. package/dist/domain/parser.js +165 -101
  111. package/dist/domain/parser.js.map +1 -1
  112. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  113. package/dist/domain/search/search/cli-formatter.js +88 -83
  114. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  115. package/dist/extractors/bash.d.ts +6 -0
  116. package/dist/extractors/bash.d.ts.map +1 -0
  117. package/dist/extractors/bash.js +91 -0
  118. package/dist/extractors/bash.js.map +1 -0
  119. package/dist/extractors/c.d.ts +6 -0
  120. package/dist/extractors/c.d.ts.map +1 -0
  121. package/dist/extractors/c.js +204 -0
  122. package/dist/extractors/c.js.map +1 -0
  123. package/dist/extractors/cpp.d.ts +6 -0
  124. package/dist/extractors/cpp.d.ts.map +1 -0
  125. package/dist/extractors/cpp.js +283 -0
  126. package/dist/extractors/cpp.js.map +1 -0
  127. package/dist/extractors/csharp.d.ts.map +1 -1
  128. package/dist/extractors/csharp.js +42 -54
  129. package/dist/extractors/csharp.js.map +1 -1
  130. package/dist/extractors/go.d.ts.map +1 -1
  131. package/dist/extractors/go.js +126 -130
  132. package/dist/extractors/go.js.map +1 -1
  133. package/dist/extractors/hcl.js +6 -6
  134. package/dist/extractors/hcl.js.map +1 -1
  135. package/dist/extractors/helpers.d.ts +32 -1
  136. package/dist/extractors/helpers.d.ts.map +1 -1
  137. package/dist/extractors/helpers.js +74 -0
  138. package/dist/extractors/helpers.js.map +1 -1
  139. package/dist/extractors/index.d.ts +6 -0
  140. package/dist/extractors/index.d.ts.map +1 -1
  141. package/dist/extractors/index.js +6 -0
  142. package/dist/extractors/index.js.map +1 -1
  143. package/dist/extractors/java.d.ts.map +1 -1
  144. package/dist/extractors/java.js +32 -47
  145. package/dist/extractors/java.js.map +1 -1
  146. package/dist/extractors/javascript.d.ts.map +1 -1
  147. package/dist/extractors/javascript.js +359 -330
  148. package/dist/extractors/javascript.js.map +1 -1
  149. package/dist/extractors/kotlin.d.ts +6 -0
  150. package/dist/extractors/kotlin.d.ts.map +1 -0
  151. package/dist/extractors/kotlin.js +275 -0
  152. package/dist/extractors/kotlin.js.map +1 -0
  153. package/dist/extractors/php.d.ts.map +1 -1
  154. package/dist/extractors/php.js +39 -44
  155. package/dist/extractors/php.js.map +1 -1
  156. package/dist/extractors/python.d.ts.map +1 -1
  157. package/dist/extractors/python.js +75 -93
  158. package/dist/extractors/python.js.map +1 -1
  159. package/dist/extractors/ruby.js +6 -13
  160. package/dist/extractors/ruby.js.map +1 -1
  161. package/dist/extractors/rust.d.ts.map +1 -1
  162. package/dist/extractors/rust.js +58 -82
  163. package/dist/extractors/rust.js.map +1 -1
  164. package/dist/extractors/scala.d.ts +6 -0
  165. package/dist/extractors/scala.d.ts.map +1 -0
  166. package/dist/extractors/scala.js +269 -0
  167. package/dist/extractors/scala.js.map +1 -0
  168. package/dist/extractors/swift.d.ts +6 -0
  169. package/dist/extractors/swift.d.ts.map +1 -0
  170. package/dist/extractors/swift.js +275 -0
  171. package/dist/extractors/swift.js.map +1 -0
  172. package/dist/features/ast.d.ts +16 -1
  173. package/dist/features/ast.d.ts.map +1 -1
  174. package/dist/features/ast.js +45 -23
  175. package/dist/features/ast.js.map +1 -1
  176. package/dist/features/audit.d.ts.map +1 -1
  177. package/dist/features/audit.js +17 -21
  178. package/dist/features/audit.js.map +1 -1
  179. package/dist/features/branch-compare.d.ts.map +1 -1
  180. package/dist/features/branch-compare.js +50 -4
  181. package/dist/features/branch-compare.js.map +1 -1
  182. package/dist/features/cfg.d.ts +7 -1
  183. package/dist/features/cfg.d.ts.map +1 -1
  184. package/dist/features/cfg.js +118 -62
  185. package/dist/features/cfg.js.map +1 -1
  186. package/dist/features/check.d.ts.map +1 -1
  187. package/dist/features/check.js +79 -62
  188. package/dist/features/check.js.map +1 -1
  189. package/dist/features/complexity-query.d.ts.map +1 -1
  190. package/dist/features/complexity-query.js +142 -137
  191. package/dist/features/complexity-query.js.map +1 -1
  192. package/dist/features/complexity.d.ts +7 -1
  193. package/dist/features/complexity.d.ts.map +1 -1
  194. package/dist/features/complexity.js +62 -1
  195. package/dist/features/complexity.js.map +1 -1
  196. package/dist/features/dataflow.d.ts +7 -1
  197. package/dist/features/dataflow.d.ts.map +1 -1
  198. package/dist/features/dataflow.js +356 -188
  199. package/dist/features/dataflow.js.map +1 -1
  200. package/dist/features/graph-enrichment.d.ts.map +1 -1
  201. package/dist/features/graph-enrichment.js +117 -104
  202. package/dist/features/graph-enrichment.js.map +1 -1
  203. package/dist/features/sequence.d.ts.map +1 -1
  204. package/dist/features/sequence.js +25 -4
  205. package/dist/features/sequence.js.map +1 -1
  206. package/dist/features/snapshot.d.ts.map +1 -1
  207. package/dist/features/snapshot.js +2 -1
  208. package/dist/features/snapshot.js.map +1 -1
  209. package/dist/features/structure-query.d.ts.map +1 -1
  210. package/dist/features/structure-query.js +29 -4
  211. package/dist/features/structure-query.js.map +1 -1
  212. package/dist/features/structure.d.ts.map +1 -1
  213. package/dist/features/structure.js +35 -15
  214. package/dist/features/structure.js.map +1 -1
  215. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  216. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  217. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  218. package/dist/graph/algorithms/leiden/index.js +43 -28
  219. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  220. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  221. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  222. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  223. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  224. package/dist/graph/algorithms/leiden/partition.js +89 -106
  225. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  226. package/dist/graph/model.d.ts +2 -0
  227. package/dist/graph/model.d.ts.map +1 -1
  228. package/dist/graph/model.js +20 -8
  229. package/dist/graph/model.js.map +1 -1
  230. package/dist/infrastructure/config.d.ts +0 -8
  231. package/dist/infrastructure/config.d.ts.map +1 -1
  232. package/dist/infrastructure/config.js +73 -62
  233. package/dist/infrastructure/config.js.map +1 -1
  234. package/dist/infrastructure/registry.d.ts +0 -8
  235. package/dist/infrastructure/registry.d.ts.map +1 -1
  236. package/dist/infrastructure/registry.js +12 -14
  237. package/dist/infrastructure/registry.js.map +1 -1
  238. package/dist/mcp/server.d.ts.map +1 -1
  239. package/dist/mcp/server.js +47 -45
  240. package/dist/mcp/server.js.map +1 -1
  241. package/dist/presentation/audit.d.ts.map +1 -1
  242. package/dist/presentation/audit.js +61 -57
  243. package/dist/presentation/audit.js.map +1 -1
  244. package/dist/presentation/branch-compare.d.ts.map +1 -1
  245. package/dist/presentation/branch-compare.js +56 -38
  246. package/dist/presentation/branch-compare.js.map +1 -1
  247. package/dist/presentation/check.d.ts.map +1 -1
  248. package/dist/presentation/check.js +30 -32
  249. package/dist/presentation/check.js.map +1 -1
  250. package/dist/presentation/colors.d.ts.map +1 -1
  251. package/dist/presentation/colors.js +2 -0
  252. package/dist/presentation/colors.js.map +1 -1
  253. package/dist/presentation/complexity.d.ts.map +1 -1
  254. package/dist/presentation/complexity.js +25 -19
  255. package/dist/presentation/complexity.js.map +1 -1
  256. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  257. package/dist/presentation/queries-cli/exports.js +15 -15
  258. package/dist/presentation/queries-cli/exports.js.map +1 -1
  259. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  260. package/dist/presentation/queries-cli/impact.js +29 -19
  261. package/dist/presentation/queries-cli/impact.js.map +1 -1
  262. package/dist/types.d.ts +406 -3
  263. package/dist/types.d.ts.map +1 -1
  264. package/grammars/tree-sitter-bash.wasm +0 -0
  265. package/grammars/tree-sitter-c.wasm +0 -0
  266. package/grammars/tree-sitter-cpp.wasm +0 -0
  267. package/grammars/tree-sitter-kotlin.wasm +0 -0
  268. package/grammars/tree-sitter-scala.wasm +0 -0
  269. package/grammars/tree-sitter-swift.wasm +0 -0
  270. package/package.json +67 -11
  271. package/src/ast-analysis/engine.ts +147 -138
  272. package/src/ast-analysis/rules/javascript.ts +1 -0
  273. package/src/ast-analysis/visitors/ast-store-visitor.ts +116 -34
  274. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  275. package/src/db/better-sqlite3.ts +20 -0
  276. package/src/db/connection.ts +148 -26
  277. package/src/db/index.ts +4 -1
  278. package/src/db/migrations.ts +38 -32
  279. package/src/db/query-builder.ts +30 -5
  280. package/src/db/repository/index.ts +1 -0
  281. package/src/db/repository/native-repository.ts +361 -0
  282. package/src/db/repository/nodes.ts +7 -3
  283. package/src/domain/analysis/context.ts +73 -75
  284. package/src/domain/analysis/dependencies.ts +78 -68
  285. package/src/domain/analysis/exports.ts +45 -34
  286. package/src/domain/analysis/fn-impact.ts +67 -64
  287. package/src/domain/analysis/module-map.ts +103 -8
  288. package/src/domain/analysis/query-helpers.ts +35 -0
  289. package/src/domain/graph/builder/context.ts +2 -0
  290. package/src/domain/graph/builder/helpers.ts +12 -6
  291. package/src/domain/graph/builder/incremental.ts +3 -2
  292. package/src/domain/graph/builder/pipeline.ts +98 -6
  293. package/src/domain/graph/builder/stages/build-edges.ts +116 -83
  294. package/src/domain/graph/builder/stages/build-structure.ts +46 -8
  295. package/src/domain/graph/builder/stages/collect-files.ts +83 -6
  296. package/src/domain/graph/builder/stages/detect-changes.ts +44 -21
  297. package/src/domain/graph/builder/stages/finalize.ts +172 -109
  298. package/src/domain/graph/builder/stages/insert-nodes.ts +147 -17
  299. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  300. package/src/domain/graph/resolve.ts +34 -46
  301. package/src/domain/graph/watcher.ts +12 -14
  302. package/src/domain/parser.ts +169 -97
  303. package/src/domain/search/search/cli-formatter.ts +121 -94
  304. package/src/extractors/bash.ts +97 -0
  305. package/src/extractors/c.ts +212 -0
  306. package/src/extractors/cpp.ts +298 -0
  307. package/src/extractors/csharp.ts +53 -56
  308. package/src/extractors/go.ts +152 -134
  309. package/src/extractors/hcl.ts +6 -6
  310. package/src/extractors/helpers.ts +93 -1
  311. package/src/extractors/index.ts +6 -0
  312. package/src/extractors/java.ts +43 -48
  313. package/src/extractors/javascript.ts +382 -317
  314. package/src/extractors/kotlin.ts +293 -0
  315. package/src/extractors/php.ts +46 -40
  316. package/src/extractors/python.ts +81 -104
  317. package/src/extractors/ruby.ts +6 -13
  318. package/src/extractors/rust.ts +65 -84
  319. package/src/extractors/scala.ts +285 -0
  320. package/src/extractors/swift.ts +293 -0
  321. package/src/features/ast.ts +74 -24
  322. package/src/features/audit.ts +24 -20
  323. package/src/features/branch-compare.ts +54 -5
  324. package/src/features/cfg.ts +158 -65
  325. package/src/features/check.ts +90 -74
  326. package/src/features/complexity-query.ts +181 -163
  327. package/src/features/complexity.ts +64 -1
  328. package/src/features/dataflow.ts +462 -217
  329. package/src/features/graph-enrichment.ts +161 -117
  330. package/src/features/sequence.ts +27 -4
  331. package/src/features/snapshot.ts +2 -1
  332. package/src/features/structure-query.ts +43 -4
  333. package/src/features/structure.ts +50 -22
  334. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  335. package/src/graph/algorithms/leiden/index.ts +67 -28
  336. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  337. package/src/graph/algorithms/leiden/partition.ts +131 -98
  338. package/src/graph/model.ts +19 -7
  339. package/src/infrastructure/config.ts +60 -58
  340. package/src/infrastructure/registry.ts +17 -14
  341. package/src/mcp/server.ts +48 -47
  342. package/src/presentation/audit.ts +72 -67
  343. package/src/presentation/branch-compare.ts +54 -50
  344. package/src/presentation/check.ts +34 -34
  345. package/src/presentation/colors.ts +2 -0
  346. package/src/presentation/complexity.ts +39 -33
  347. package/src/presentation/queries-cli/exports.ts +17 -17
  348. package/src/presentation/queries-cli/impact.ts +30 -22
  349. package/src/types.ts +458 -3
@@ -0,0 +1,361 @@
1
+ /**
2
+ * NativeRepository — delegates all Repository read methods to NativeDatabase (rusqlite via napi-rs).
3
+ *
4
+ * Phase 6.14: every query runs via rusqlite when the native engine is available.
5
+ * Falls back to SqliteRepository (better-sqlite3) when native is unavailable.
6
+ *
7
+ * napi-rs converts Rust snake_case fields to JS camelCase. This class maps them
8
+ * back to the snake_case field names that the Repository interface expects.
9
+ */
10
+
11
+ import { ConfigError } from '../../shared/errors.js';
12
+ import type {
13
+ AdjacentEdgeRow,
14
+ CallableNodeRow,
15
+ CallEdgeRow,
16
+ ChildNodeRow,
17
+ ComplexityMetrics,
18
+ FileNodeRow,
19
+ ImportEdgeRow,
20
+ ImportGraphEdgeRow,
21
+ IntraFileCallEdge,
22
+ ListFunctionOpts,
23
+ NativeAdjacentEdgeRow,
24
+ NativeCallableNodeRow,
25
+ NativeCallEdgeRow,
26
+ NativeChildNodeRow,
27
+ NativeComplexityMetrics,
28
+ NativeDatabase,
29
+ NativeFileNodeRow,
30
+ NativeImportEdgeRow,
31
+ NativeImportGraphEdgeRow,
32
+ NativeIntraFileCallEdge,
33
+ NativeNodeIdRow,
34
+ NativeNodeRow,
35
+ NativeNodeRowWithFanIn,
36
+ NativeRelatedNodeRow,
37
+ NativeTriageNodeRow,
38
+ NodeIdRow,
39
+ NodeRow,
40
+ NodeRowWithFanIn,
41
+ QueryOpts,
42
+ RelatedNodeRow,
43
+ TriageNodeRow,
44
+ TriageQueryOpts,
45
+ } from '../../types.js';
46
+ import { Repository } from './base.js';
47
+
48
+ // ── Row converters (napi camelCase → Repository snake_case) ─────────────
49
+
50
+ function toNodeRow(r: NativeNodeRow): NodeRow {
51
+ return {
52
+ id: r.id,
53
+ name: r.name,
54
+ kind: r.kind as NodeRow['kind'],
55
+ file: r.file,
56
+ line: r.line ?? 0,
57
+ end_line: r.endLine ?? null,
58
+ parent_id: r.parentId ?? null,
59
+ exported: (r.exported ?? null) as 0 | 1 | null,
60
+ qualified_name: r.qualifiedName ?? null,
61
+ scope: r.scope ?? null,
62
+ visibility: (r.visibility ?? null) as NodeRow['visibility'],
63
+ role: (r.role ?? null) as NodeRow['role'],
64
+ };
65
+ }
66
+
67
+ function toNodeRowWithFanIn(r: NativeNodeRowWithFanIn): NodeRowWithFanIn {
68
+ return { ...toNodeRow(r), fan_in: r.fanIn };
69
+ }
70
+
71
+ function toTriageNodeRow(r: NativeTriageNodeRow): TriageNodeRow {
72
+ return {
73
+ ...toNodeRow(r),
74
+ fan_in: r.fanIn,
75
+ cognitive: r.cognitive,
76
+ mi: r.mi,
77
+ cyclomatic: r.cyclomatic,
78
+ max_nesting: r.maxNesting,
79
+ churn: r.churn,
80
+ };
81
+ }
82
+
83
+ function toNodeIdRow(r: NativeNodeIdRow): NodeIdRow {
84
+ return { id: r.id, name: r.name, kind: r.kind, line: r.line ?? 0 };
85
+ }
86
+
87
+ function toChildNodeRow(r: NativeChildNodeRow): ChildNodeRow {
88
+ return {
89
+ name: r.name,
90
+ kind: r.kind as ChildNodeRow['kind'],
91
+ line: r.line ?? 0,
92
+ end_line: r.endLine ?? null,
93
+ qualified_name: r.qualifiedName ?? null,
94
+ scope: r.scope ?? null,
95
+ visibility: (r.visibility ?? null) as ChildNodeRow['visibility'],
96
+ };
97
+ }
98
+
99
+ function toRelatedNodeRow(r: NativeRelatedNodeRow): RelatedNodeRow {
100
+ return {
101
+ id: r.id,
102
+ name: r.name,
103
+ kind: r.kind,
104
+ file: r.file,
105
+ line: r.line ?? 0,
106
+ end_line: r.endLine,
107
+ };
108
+ }
109
+
110
+ function toAdjacentEdgeRow(r: NativeAdjacentEdgeRow): AdjacentEdgeRow {
111
+ return {
112
+ name: r.name,
113
+ kind: r.kind,
114
+ file: r.file,
115
+ line: r.line ?? 0,
116
+ edge_kind: r.edgeKind as AdjacentEdgeRow['edge_kind'],
117
+ };
118
+ }
119
+
120
+ function toImportEdgeRow(r: NativeImportEdgeRow): ImportEdgeRow {
121
+ return { file: r.file, edge_kind: r.edgeKind as ImportEdgeRow['edge_kind'] };
122
+ }
123
+
124
+ function toIntraFileCallEdge(r: NativeIntraFileCallEdge): IntraFileCallEdge {
125
+ return { caller_name: r.callerName, callee_name: r.calleeName };
126
+ }
127
+
128
+ function toCallableNodeRow(r: NativeCallableNodeRow): CallableNodeRow {
129
+ return { id: r.id, name: r.name, kind: r.kind, file: r.file };
130
+ }
131
+
132
+ function toCallEdgeRow(r: NativeCallEdgeRow): CallEdgeRow {
133
+ return {
134
+ source_id: r.sourceId,
135
+ target_id: r.targetId,
136
+ confidence: r.confidence,
137
+ };
138
+ }
139
+
140
+ function toFileNodeRow(r: NativeFileNodeRow): FileNodeRow {
141
+ return { id: r.id, name: r.name, file: r.file };
142
+ }
143
+
144
+ function toImportGraphEdgeRow(r: NativeImportGraphEdgeRow): ImportGraphEdgeRow {
145
+ return { source_id: r.sourceId, target_id: r.targetId };
146
+ }
147
+
148
+ function toComplexityMetrics(r: NativeComplexityMetrics): ComplexityMetrics {
149
+ return {
150
+ cognitive: r.cognitive,
151
+ cyclomatic: r.cyclomatic,
152
+ max_nesting: r.maxNesting,
153
+ maintainability_index: r.maintainabilityIndex ?? null,
154
+ halstead_volume: r.halsteadVolume ?? null,
155
+ };
156
+ }
157
+
158
+ // ── NativeRepository ────────────────────────────────────────────────────
159
+
160
+ export class NativeRepository extends Repository {
161
+ #ndb: NativeDatabase;
162
+
163
+ constructor(ndb: NativeDatabase) {
164
+ super();
165
+ this.#ndb = ndb;
166
+ }
167
+
168
+ // ── Node lookups ──────────────────────────────────────────────────
169
+
170
+ findNodeById(id: number): NodeRow | undefined {
171
+ const r = this.#ndb.findNodeById(id);
172
+ return r ? toNodeRow(r) : undefined;
173
+ }
174
+
175
+ findNodesByFile(file: string): NodeRow[] {
176
+ return this.#ndb.findNodesByFile(file).map(toNodeRow);
177
+ }
178
+
179
+ findFileNodes(fileLike: string): NodeRow[] {
180
+ return this.#ndb.findFileNodes(fileLike).map(toNodeRow);
181
+ }
182
+
183
+ findNodesWithFanIn(namePattern: string, opts: QueryOpts = {}): NodeRowWithFanIn[] {
184
+ return this.#ndb
185
+ .findNodesWithFanIn(namePattern, opts.kinds ?? null, opts.file ?? null)
186
+ .map(toNodeRowWithFanIn);
187
+ }
188
+
189
+ countNodes(): number {
190
+ return this.#ndb.countNodes();
191
+ }
192
+
193
+ countEdges(): number {
194
+ return this.#ndb.countEdges();
195
+ }
196
+
197
+ countFiles(): number {
198
+ return this.#ndb.countFiles();
199
+ }
200
+
201
+ getNodeId(name: string, kind: string, file: string, line: number): number | undefined {
202
+ return this.#ndb.getNodeId(name, kind, file, line) ?? undefined;
203
+ }
204
+
205
+ getFunctionNodeId(name: string, file: string, line: number): number | undefined {
206
+ return this.#ndb.getFunctionNodeId(name, file, line) ?? undefined;
207
+ }
208
+
209
+ bulkNodeIdsByFile(file: string): NodeIdRow[] {
210
+ return this.#ndb.bulkNodeIdsByFile(file).map(toNodeIdRow);
211
+ }
212
+
213
+ findNodeChildren(parentId: number): ChildNodeRow[] {
214
+ return this.#ndb.findNodeChildren(parentId).map(toChildNodeRow);
215
+ }
216
+
217
+ findNodesByScope(scopeName: string, opts: QueryOpts = {}): NodeRow[] {
218
+ return this.#ndb
219
+ .findNodesByScope(scopeName, opts.kind ?? null, opts.file ?? null)
220
+ .map(toNodeRow);
221
+ }
222
+
223
+ findNodeByQualifiedName(qualifiedName: string, opts: { file?: string } = {}): NodeRow[] {
224
+ return this.#ndb.findNodeByQualifiedName(qualifiedName, opts.file ?? null).map(toNodeRow);
225
+ }
226
+
227
+ listFunctionNodes(opts: ListFunctionOpts = {}): NodeRow[] {
228
+ return this.#ndb
229
+ .listFunctionNodes(opts.file ?? null, opts.pattern ?? null, opts.noTests ?? null)
230
+ .map(toNodeRow);
231
+ }
232
+
233
+ iterateFunctionNodes(opts: ListFunctionOpts = {}): IterableIterator<NodeRow> {
234
+ const rows = this.#ndb
235
+ .iterateFunctionNodes(opts.file ?? null, opts.pattern ?? null, opts.noTests ?? null)
236
+ .map(toNodeRow);
237
+ return rows[Symbol.iterator]();
238
+ }
239
+
240
+ findNodesForTriage(opts: TriageQueryOpts = {}): TriageNodeRow[] {
241
+ try {
242
+ return this.#ndb
243
+ .findNodesForTriage(
244
+ opts.kind ?? null,
245
+ opts.role ?? null,
246
+ opts.file ?? null,
247
+ opts.noTests ?? null,
248
+ )
249
+ .map(toTriageNodeRow);
250
+ } catch (e: unknown) {
251
+ const msg = (e as Error).message;
252
+ if (msg.startsWith('Invalid kind:') || msg.startsWith('Invalid role:')) {
253
+ throw new ConfigError(msg);
254
+ }
255
+ throw e;
256
+ }
257
+ }
258
+
259
+ // ── Edge queries ──────────────────────────────────────────────────
260
+
261
+ findCallees(nodeId: number): RelatedNodeRow[] {
262
+ return this.#ndb.findCallees(nodeId).map(toRelatedNodeRow);
263
+ }
264
+
265
+ findCallers(nodeId: number): RelatedNodeRow[] {
266
+ return this.#ndb.findCallers(nodeId).map(toRelatedNodeRow);
267
+ }
268
+
269
+ findDistinctCallers(nodeId: number): RelatedNodeRow[] {
270
+ return this.#ndb.findDistinctCallers(nodeId).map(toRelatedNodeRow);
271
+ }
272
+
273
+ findAllOutgoingEdges(nodeId: number): AdjacentEdgeRow[] {
274
+ return this.#ndb.findAllOutgoingEdges(nodeId).map(toAdjacentEdgeRow);
275
+ }
276
+
277
+ findAllIncomingEdges(nodeId: number): AdjacentEdgeRow[] {
278
+ return this.#ndb.findAllIncomingEdges(nodeId).map(toAdjacentEdgeRow);
279
+ }
280
+
281
+ findCalleeNames(nodeId: number): string[] {
282
+ return this.#ndb.findCalleeNames(nodeId);
283
+ }
284
+
285
+ findCallerNames(nodeId: number): string[] {
286
+ return this.#ndb.findCallerNames(nodeId);
287
+ }
288
+
289
+ findImportTargets(nodeId: number): ImportEdgeRow[] {
290
+ return this.#ndb.findImportTargets(nodeId).map(toImportEdgeRow);
291
+ }
292
+
293
+ findImportSources(nodeId: number): ImportEdgeRow[] {
294
+ return this.#ndb.findImportSources(nodeId).map(toImportEdgeRow);
295
+ }
296
+
297
+ findImportDependents(nodeId: number): NodeRow[] {
298
+ return this.#ndb.findImportDependents(nodeId).map(toNodeRow);
299
+ }
300
+
301
+ findCrossFileCallTargets(file: string): Set<number> {
302
+ return new Set(this.#ndb.findCrossFileCallTargets(file));
303
+ }
304
+
305
+ countCrossFileCallers(nodeId: number, file: string): number {
306
+ return this.#ndb.countCrossFileCallers(nodeId, file);
307
+ }
308
+
309
+ getClassHierarchy(classNodeId: number): Set<number> {
310
+ return new Set(this.#ndb.getClassHierarchy(classNodeId));
311
+ }
312
+
313
+ findImplementors(nodeId: number): RelatedNodeRow[] {
314
+ return this.#ndb.findImplementors(nodeId).map(toRelatedNodeRow);
315
+ }
316
+
317
+ findInterfaces(nodeId: number): RelatedNodeRow[] {
318
+ return this.#ndb.findInterfaces(nodeId).map(toRelatedNodeRow);
319
+ }
320
+
321
+ findIntraFileCallEdges(file: string): IntraFileCallEdge[] {
322
+ return this.#ndb.findIntraFileCallEdges(file).map(toIntraFileCallEdge);
323
+ }
324
+
325
+ // ── Graph-read queries ────────────────────────────────────────────
326
+
327
+ getCallableNodes(): CallableNodeRow[] {
328
+ return this.#ndb.getCallableNodes().map(toCallableNodeRow);
329
+ }
330
+
331
+ getCallEdges(): CallEdgeRow[] {
332
+ return this.#ndb.getCallEdges().map(toCallEdgeRow);
333
+ }
334
+
335
+ getFileNodesAll(): FileNodeRow[] {
336
+ return this.#ndb.getFileNodesAll().map(toFileNodeRow);
337
+ }
338
+
339
+ getImportEdges(): ImportGraphEdgeRow[] {
340
+ return this.#ndb.getImportEdges().map(toImportGraphEdgeRow);
341
+ }
342
+
343
+ // ── Optional table checks ─────────────────────────────────────────
344
+
345
+ hasCfgTables(): boolean {
346
+ return this.#ndb.hasCfgTables();
347
+ }
348
+
349
+ hasEmbeddings(): boolean {
350
+ return this.#ndb.hasEmbeddings();
351
+ }
352
+
353
+ hasDataflowTable(): boolean {
354
+ return this.#ndb.hasDataflowTable();
355
+ }
356
+
357
+ getComplexityForNode(nodeId: number): ComplexityMetrics | undefined {
358
+ const r = this.#ndb.getComplexityForNode(nodeId);
359
+ return r ? toComplexityMetrics(r) : undefined;
360
+ }
361
+ }
@@ -4,6 +4,7 @@ import type {
4
4
  BetterSqlite3Database,
5
5
  ChildNodeRow,
6
6
  ListFunctionOpts,
7
+ NativeDatabase,
7
8
  NodeIdRow,
8
9
  NodeRow,
9
10
  NodeRowWithFanIn,
@@ -24,6 +25,7 @@ export function findNodesWithFanIn(
24
25
  db: BetterSqlite3Database,
25
26
  namePattern: string,
26
27
  opts: QueryOpts = {},
28
+ nativeDb?: NativeDatabase,
27
29
  ): NodeRowWithFanIn[] {
28
30
  const q = new NodeQuery()
29
31
  .select('n.*, COALESCE(fi.cnt, 0) AS fan_in')
@@ -37,7 +39,7 @@ export function findNodesWithFanIn(
37
39
  q.fileFilter(opts.file);
38
40
  }
39
41
 
40
- return q.all(db);
42
+ return q.all(db, nativeDb);
41
43
  }
42
44
 
43
45
  /**
@@ -46,6 +48,7 @@ export function findNodesWithFanIn(
46
48
  export function findNodesForTriage(
47
49
  db: BetterSqlite3Database,
48
50
  opts: TriageQueryOpts = {},
51
+ nativeDb?: NativeDatabase,
49
52
  ): TriageNodeRow[] {
50
53
  if (opts.kind && !(EVERY_SYMBOL_KIND as readonly string[]).includes(opts.kind)) {
51
54
  throw new ConfigError(
@@ -77,7 +80,7 @@ export function findNodesForTriage(
77
80
  .roleFilter(opts.role)
78
81
  .orderBy('n.file, n.line');
79
82
 
80
- return q.all(db);
83
+ return q.all(db, nativeDb);
81
84
  }
82
85
 
83
86
  /**
@@ -99,8 +102,9 @@ function _functionNodeQuery(opts: ListFunctionOpts = {}): InstanceType<typeof No
99
102
  export function listFunctionNodes(
100
103
  db: BetterSqlite3Database,
101
104
  opts: ListFunctionOpts = {},
105
+ nativeDb?: NativeDatabase,
102
106
  ): NodeRow[] {
103
- return _functionNodeQuery(opts).all(db);
107
+ return _functionNodeQuery(opts).all(db, nativeDb);
104
108
  }
105
109
 
106
110
  /**
@@ -15,10 +15,8 @@ import {
15
15
  getComplexityForNode,
16
16
  getLineCountForNode,
17
17
  getMaxEndLineForFile,
18
- openReadonlyOrFail,
19
18
  } from '../../db/index.js';
20
19
  import { cachedStmt } from '../../db/repository/cached-stmt.js';
21
- import { loadConfig } from '../../infrastructure/config.js';
22
20
  import { debug } from '../../infrastructure/logger.js';
23
21
  import { isTestFile } from '../../infrastructure/test-filter.js';
24
22
  import {
@@ -40,6 +38,7 @@ import type {
40
38
  RelatedNodeRow,
41
39
  StmtCache,
42
40
  } from '../../types.js';
41
+ import { resolveAnalysisOpts, withReadonlyDb } from './query-helpers.js';
43
42
  import { findMatchingNodes } from './symbol-lookup.js';
44
43
 
45
44
  interface DisplayOpts {
@@ -52,6 +51,60 @@ interface DisplayOpts {
52
51
  [key: string]: unknown;
53
52
  }
54
53
 
54
+ /** Format a callee row into the output shape with summary and source. */
55
+ function formatCalleeRow(
56
+ c: RelatedNodeRow,
57
+ repoRoot: string,
58
+ getFileLines: (file: string) => string[] | null,
59
+ displayOpts: DisplayOpts,
60
+ includeSource: boolean,
61
+ ) {
62
+ const cLines = getFileLines(c.file);
63
+ return {
64
+ name: c.name,
65
+ kind: c.kind,
66
+ file: c.file,
67
+ line: c.line,
68
+ endLine: c.end_line || null,
69
+ summary: cLines ? extractSummary(cLines, c.line, displayOpts) : null,
70
+ source: includeSource
71
+ ? readSourceRange(repoRoot, c.file, c.line, c.end_line ?? undefined, displayOpts)
72
+ : null,
73
+ };
74
+ }
75
+
76
+ /** BFS to collect deeper callees beyond the first level. */
77
+ function collectDeeperCallees(
78
+ db: BetterSqlite3Database,
79
+ startIds: number[],
80
+ rootId: number,
81
+ repoRoot: string,
82
+ getFileLines: (file: string) => string[] | null,
83
+ opts: { noTests: boolean; maxDepth: number; displayOpts: DisplayOpts },
84
+ ) {
85
+ const { noTests, maxDepth, displayOpts } = opts;
86
+ const visited = new Set(startIds);
87
+ visited.add(rootId);
88
+ let frontier = [...startIds];
89
+ const result: ReturnType<typeof formatCalleeRow>[] = [];
90
+
91
+ for (let d = 2; d <= maxDepth; d++) {
92
+ const nextFrontier: number[] = [];
93
+ for (const fid of frontier) {
94
+ const deeper = findCallees(db, fid) as RelatedNodeRow[];
95
+ for (const c of deeper) {
96
+ if (visited.has(c.id) || (noTests && isTestFile(c.file))) continue;
97
+ visited.add(c.id);
98
+ nextFrontier.push(c.id);
99
+ result.push(formatCalleeRow(c, repoRoot, getFileLines, displayOpts, true));
100
+ }
101
+ }
102
+ frontier = nextFrontier;
103
+ if (frontier.length === 0) break;
104
+ }
105
+ return result;
106
+ }
107
+
55
108
  function buildCallees(
56
109
  db: BetterSqlite3Database,
57
110
  node: NodeRow,
@@ -63,65 +116,20 @@ function buildCallees(
63
116
  const calleeRows = findCallees(db, node.id) as RelatedNodeRow[];
64
117
  const filteredCallees = noTests ? calleeRows.filter((c) => !isTestFile(c.file)) : calleeRows;
65
118
 
66
- const callees = filteredCallees.map((c) => {
67
- const cLines = getFileLines(c.file);
68
- const summary = cLines ? extractSummary(cLines, c.line, displayOpts) : null;
69
- let calleeSource: string | null = null;
70
- if (depth >= 1) {
71
- calleeSource = readSourceRange(
72
- repoRoot,
73
- c.file,
74
- c.line,
75
- c.end_line ?? undefined,
76
- displayOpts,
77
- );
78
- }
79
- return {
80
- name: c.name,
81
- kind: c.kind,
82
- file: c.file,
83
- line: c.line,
84
- endLine: c.end_line || null,
85
- summary,
86
- source: calleeSource,
87
- };
88
- });
119
+ const callees = filteredCallees.map((c) =>
120
+ formatCalleeRow(c, repoRoot, getFileLines, displayOpts, depth >= 1),
121
+ );
89
122
 
90
123
  if (depth > 1) {
91
- const visited = new Set(filteredCallees.map((c) => c.id));
92
- visited.add(node.id);
93
- let frontier = filteredCallees.map((c) => c.id);
94
- const maxDepth = Math.min(depth, 5);
95
- for (let d = 2; d <= maxDepth; d++) {
96
- const nextFrontier: number[] = [];
97
- for (const fid of frontier) {
98
- const deeper = findCallees(db, fid) as RelatedNodeRow[];
99
- for (const c of deeper) {
100
- if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
101
- visited.add(c.id);
102
- nextFrontier.push(c.id);
103
- const cLines = getFileLines(c.file);
104
- callees.push({
105
- name: c.name,
106
- kind: c.kind,
107
- file: c.file,
108
- line: c.line,
109
- endLine: c.end_line || null,
110
- summary: cLines ? extractSummary(cLines, c.line, displayOpts) : null,
111
- source: readSourceRange(
112
- repoRoot,
113
- c.file,
114
- c.line,
115
- c.end_line ?? undefined,
116
- displayOpts,
117
- ),
118
- });
119
- }
120
- }
121
- }
122
- frontier = nextFrontier;
123
- if (frontier.length === 0) break;
124
- }
124
+ const deeper = collectDeeperCallees(
125
+ db,
126
+ filteredCallees.map((c) => c.id),
127
+ node.id,
128
+ repoRoot,
129
+ getFileLines,
130
+ { noTests, maxDepth: Math.min(depth, 5), displayOpts },
131
+ );
132
+ callees.push(...deeper);
125
133
  }
126
134
 
127
135
  return callees;
@@ -433,15 +441,12 @@ export function contextData(
433
441
  config?: any;
434
442
  } = {},
435
443
  ) {
436
- const db = openReadonlyOrFail(customDbPath);
437
- try {
444
+ return withReadonlyDb(customDbPath, (db) => {
438
445
  const depth = opts.depth || 0;
439
446
  const noSource = opts.noSource || false;
440
- const noTests = opts.noTests || false;
441
447
  const includeTests = opts.includeTests || false;
442
448
 
443
- const config = opts.config || loadConfig();
444
- const displayOpts: DisplayOpts = config.display || {};
449
+ const { noTests, displayOpts } = resolveAnalysisOpts(opts);
445
450
 
446
451
  const dbPath = findDbPath(customDbPath);
447
452
  const repoRoot = path.resolve(path.dirname(dbPath), '..');
@@ -494,9 +499,7 @@ export function contextData(
494
499
 
495
500
  const base = { name, results };
496
501
  return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
497
- } finally {
498
- db.close();
499
- }
502
+ });
500
503
  }
501
504
 
502
505
  export function explainData(
@@ -510,14 +513,11 @@ export function explainData(
510
513
  config?: any;
511
514
  } = {},
512
515
  ) {
513
- const db = openReadonlyOrFail(customDbPath);
514
- try {
515
- const noTests = opts.noTests || false;
516
+ return withReadonlyDb(customDbPath, (db) => {
516
517
  const depth = opts.depth || 0;
517
518
  const kind = isFileLikeTarget(target) ? 'file' : 'function';
518
519
 
519
- const config = opts.config || loadConfig();
520
- const displayOpts: DisplayOpts = config.display || {};
520
+ const { noTests, displayOpts } = resolveAnalysisOpts(opts);
521
521
 
522
522
  const dbPath = findDbPath(customDbPath);
523
523
  const repoRoot = path.resolve(path.dirname(dbPath), '..');
@@ -536,7 +536,5 @@ export function explainData(
536
536
 
537
537
  const base = { target, kind, results };
538
538
  return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
539
- } finally {
540
- db.close();
541
- }
539
+ });
542
540
  }