@optave/codegraph 3.5.0 → 3.7.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 (346) hide show
  1. package/README.md +47 -21
  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/visitors/ast-store-visitor.d.ts.map +1 -1
  6. package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
  7. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  8. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  10. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  11. package/dist/db/connection.d.ts +12 -2
  12. package/dist/db/connection.d.ts.map +1 -1
  13. package/dist/db/connection.js +81 -53
  14. package/dist/db/connection.js.map +1 -1
  15. package/dist/db/index.d.ts +1 -1
  16. package/dist/db/index.d.ts.map +1 -1
  17. package/dist/db/index.js +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/db/migrations.d.ts.map +1 -1
  20. package/dist/db/migrations.js +38 -32
  21. package/dist/db/migrations.js.map +1 -1
  22. package/dist/domain/analysis/context.d.ts.map +1 -1
  23. package/dist/domain/analysis/context.js +51 -66
  24. package/dist/domain/analysis/context.js.map +1 -1
  25. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  26. package/dist/domain/analysis/dependencies.js +62 -70
  27. package/dist/domain/analysis/dependencies.js.map +1 -1
  28. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  29. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  30. package/dist/domain/analysis/exports.d.ts.map +1 -1
  31. package/dist/domain/analysis/exports.js +29 -33
  32. package/dist/domain/analysis/exports.js.map +1 -1
  33. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  34. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  35. package/dist/domain/analysis/fn-impact.js +35 -65
  36. package/dist/domain/analysis/fn-impact.js.map +1 -1
  37. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  38. package/dist/domain/analysis/module-map.js +91 -6
  39. package/dist/domain/analysis/module-map.js.map +1 -1
  40. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  41. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  42. package/dist/domain/analysis/query-helpers.js +27 -0
  43. package/dist/domain/analysis/query-helpers.js.map +1 -0
  44. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  45. package/dist/domain/graph/builder/helpers.js +15 -9
  46. package/dist/domain/graph/builder/helpers.js.map +1 -1
  47. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  48. package/dist/domain/graph/builder/incremental.js +3 -2
  49. package/dist/domain/graph/builder/incremental.js.map +1 -1
  50. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  51. package/dist/domain/graph/builder/pipeline.js +69 -3
  52. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  53. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  54. package/dist/domain/graph/builder/stages/build-edges.js +7 -51
  55. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  56. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  57. package/dist/domain/graph/builder/stages/build-structure.js +7 -5
  58. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  59. package/dist/domain/graph/builder/stages/collect-files.js +2 -2
  60. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  61. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  62. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  63. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  64. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  65. package/dist/domain/graph/builder/stages/finalize.js +124 -105
  66. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  67. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  68. package/dist/domain/graph/builder/stages/insert-nodes.js +28 -15
  69. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  70. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  71. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  72. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  73. package/dist/domain/graph/resolve.d.ts +0 -4
  74. package/dist/domain/graph/resolve.d.ts.map +1 -1
  75. package/dist/domain/graph/resolve.js +32 -48
  76. package/dist/domain/graph/resolve.js.map +1 -1
  77. package/dist/domain/graph/watcher.d.ts.map +1 -1
  78. package/dist/domain/graph/watcher.js +12 -12
  79. package/dist/domain/graph/watcher.js.map +1 -1
  80. package/dist/domain/parser.d.ts +1 -1
  81. package/dist/domain/parser.d.ts.map +1 -1
  82. package/dist/domain/parser.js +206 -101
  83. package/dist/domain/parser.js.map +1 -1
  84. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  85. package/dist/domain/search/search/cli-formatter.js +88 -83
  86. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  87. package/dist/extractors/bash.d.ts +6 -0
  88. package/dist/extractors/bash.d.ts.map +1 -0
  89. package/dist/extractors/bash.js +91 -0
  90. package/dist/extractors/bash.js.map +1 -0
  91. package/dist/extractors/c.d.ts +6 -0
  92. package/dist/extractors/c.d.ts.map +1 -0
  93. package/dist/extractors/c.js +204 -0
  94. package/dist/extractors/c.js.map +1 -0
  95. package/dist/extractors/cpp.d.ts +6 -0
  96. package/dist/extractors/cpp.d.ts.map +1 -0
  97. package/dist/extractors/cpp.js +283 -0
  98. package/dist/extractors/cpp.js.map +1 -0
  99. package/dist/extractors/csharp.d.ts.map +1 -1
  100. package/dist/extractors/csharp.js +42 -54
  101. package/dist/extractors/csharp.js.map +1 -1
  102. package/dist/extractors/dart.d.ts +6 -0
  103. package/dist/extractors/dart.d.ts.map +1 -0
  104. package/dist/extractors/dart.js +277 -0
  105. package/dist/extractors/dart.js.map +1 -0
  106. package/dist/extractors/elixir.d.ts +9 -0
  107. package/dist/extractors/elixir.d.ts.map +1 -0
  108. package/dist/extractors/elixir.js +223 -0
  109. package/dist/extractors/elixir.js.map +1 -0
  110. package/dist/extractors/go.d.ts.map +1 -1
  111. package/dist/extractors/go.js +126 -130
  112. package/dist/extractors/go.js.map +1 -1
  113. package/dist/extractors/haskell.d.ts +8 -0
  114. package/dist/extractors/haskell.d.ts.map +1 -0
  115. package/dist/extractors/haskell.js +217 -0
  116. package/dist/extractors/haskell.js.map +1 -0
  117. package/dist/extractors/hcl.js +6 -6
  118. package/dist/extractors/hcl.js.map +1 -1
  119. package/dist/extractors/helpers.d.ts +32 -1
  120. package/dist/extractors/helpers.d.ts.map +1 -1
  121. package/dist/extractors/helpers.js +74 -0
  122. package/dist/extractors/helpers.js.map +1 -1
  123. package/dist/extractors/index.d.ts +12 -0
  124. package/dist/extractors/index.d.ts.map +1 -1
  125. package/dist/extractors/index.js +12 -0
  126. package/dist/extractors/index.js.map +1 -1
  127. package/dist/extractors/java.d.ts.map +1 -1
  128. package/dist/extractors/java.js +32 -47
  129. package/dist/extractors/java.js.map +1 -1
  130. package/dist/extractors/javascript.d.ts.map +1 -1
  131. package/dist/extractors/javascript.js +306 -292
  132. package/dist/extractors/javascript.js.map +1 -1
  133. package/dist/extractors/kotlin.d.ts +6 -0
  134. package/dist/extractors/kotlin.d.ts.map +1 -0
  135. package/dist/extractors/kotlin.js +275 -0
  136. package/dist/extractors/kotlin.js.map +1 -0
  137. package/dist/extractors/lua.d.ts +6 -0
  138. package/dist/extractors/lua.d.ts.map +1 -0
  139. package/dist/extractors/lua.js +162 -0
  140. package/dist/extractors/lua.js.map +1 -0
  141. package/dist/extractors/ocaml.d.ts +6 -0
  142. package/dist/extractors/ocaml.d.ts.map +1 -0
  143. package/dist/extractors/ocaml.js +236 -0
  144. package/dist/extractors/ocaml.js.map +1 -0
  145. package/dist/extractors/php.d.ts.map +1 -1
  146. package/dist/extractors/php.js +39 -44
  147. package/dist/extractors/php.js.map +1 -1
  148. package/dist/extractors/python.d.ts.map +1 -1
  149. package/dist/extractors/python.js +75 -93
  150. package/dist/extractors/python.js.map +1 -1
  151. package/dist/extractors/ruby.js +6 -13
  152. package/dist/extractors/ruby.js.map +1 -1
  153. package/dist/extractors/rust.d.ts.map +1 -1
  154. package/dist/extractors/rust.js +58 -83
  155. package/dist/extractors/rust.js.map +1 -1
  156. package/dist/extractors/scala.d.ts +6 -0
  157. package/dist/extractors/scala.d.ts.map +1 -0
  158. package/dist/extractors/scala.js +269 -0
  159. package/dist/extractors/scala.js.map +1 -0
  160. package/dist/extractors/swift.d.ts +6 -0
  161. package/dist/extractors/swift.d.ts.map +1 -0
  162. package/dist/extractors/swift.js +275 -0
  163. package/dist/extractors/swift.js.map +1 -0
  164. package/dist/extractors/zig.d.ts +9 -0
  165. package/dist/extractors/zig.d.ts.map +1 -0
  166. package/dist/extractors/zig.js +276 -0
  167. package/dist/extractors/zig.js.map +1 -0
  168. package/dist/features/ast.d.ts +2 -0
  169. package/dist/features/ast.d.ts.map +1 -1
  170. package/dist/features/ast.js +9 -24
  171. package/dist/features/ast.js.map +1 -1
  172. package/dist/features/audit.d.ts.map +1 -1
  173. package/dist/features/audit.js +17 -21
  174. package/dist/features/audit.js.map +1 -1
  175. package/dist/features/branch-compare.d.ts.map +1 -1
  176. package/dist/features/branch-compare.js +47 -3
  177. package/dist/features/branch-compare.js.map +1 -1
  178. package/dist/features/cfg.d.ts +7 -1
  179. package/dist/features/cfg.d.ts.map +1 -1
  180. package/dist/features/cfg.js +72 -61
  181. package/dist/features/cfg.js.map +1 -1
  182. package/dist/features/check.d.ts.map +1 -1
  183. package/dist/features/check.js +79 -62
  184. package/dist/features/check.js.map +1 -1
  185. package/dist/features/complexity-query.d.ts.map +1 -1
  186. package/dist/features/complexity-query.js +142 -137
  187. package/dist/features/complexity-query.js.map +1 -1
  188. package/dist/features/complexity.d.ts +7 -1
  189. package/dist/features/complexity.d.ts.map +1 -1
  190. package/dist/features/complexity.js +62 -1
  191. package/dist/features/complexity.js.map +1 -1
  192. package/dist/features/dataflow.d.ts +7 -1
  193. package/dist/features/dataflow.d.ts.map +1 -1
  194. package/dist/features/dataflow.js +356 -188
  195. package/dist/features/dataflow.js.map +1 -1
  196. package/dist/features/graph-enrichment.d.ts.map +1 -1
  197. package/dist/features/graph-enrichment.js +117 -104
  198. package/dist/features/graph-enrichment.js.map +1 -1
  199. package/dist/features/sequence.d.ts.map +1 -1
  200. package/dist/features/sequence.js +25 -4
  201. package/dist/features/sequence.js.map +1 -1
  202. package/dist/features/structure-query.d.ts.map +1 -1
  203. package/dist/features/structure-query.js +29 -4
  204. package/dist/features/structure-query.js.map +1 -1
  205. package/dist/features/structure.d.ts.map +1 -1
  206. package/dist/features/structure.js +35 -15
  207. package/dist/features/structure.js.map +1 -1
  208. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  209. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  210. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  211. package/dist/graph/algorithms/leiden/index.js +43 -28
  212. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  213. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  214. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  215. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  216. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  217. package/dist/graph/algorithms/leiden/partition.js +89 -106
  218. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  219. package/dist/graph/model.d.ts +2 -0
  220. package/dist/graph/model.d.ts.map +1 -1
  221. package/dist/graph/model.js +20 -8
  222. package/dist/graph/model.js.map +1 -1
  223. package/dist/infrastructure/config.d.ts +0 -8
  224. package/dist/infrastructure/config.d.ts.map +1 -1
  225. package/dist/infrastructure/config.js +73 -62
  226. package/dist/infrastructure/config.js.map +1 -1
  227. package/dist/infrastructure/registry.d.ts +0 -8
  228. package/dist/infrastructure/registry.d.ts.map +1 -1
  229. package/dist/infrastructure/registry.js +12 -14
  230. package/dist/infrastructure/registry.js.map +1 -1
  231. package/dist/mcp/server.d.ts.map +1 -1
  232. package/dist/mcp/server.js +45 -36
  233. package/dist/mcp/server.js.map +1 -1
  234. package/dist/presentation/audit.d.ts.map +1 -1
  235. package/dist/presentation/audit.js +61 -57
  236. package/dist/presentation/audit.js.map +1 -1
  237. package/dist/presentation/branch-compare.d.ts.map +1 -1
  238. package/dist/presentation/branch-compare.js +56 -38
  239. package/dist/presentation/branch-compare.js.map +1 -1
  240. package/dist/presentation/check.d.ts.map +1 -1
  241. package/dist/presentation/check.js +30 -32
  242. package/dist/presentation/check.js.map +1 -1
  243. package/dist/presentation/colors.d.ts.map +1 -1
  244. package/dist/presentation/colors.js +2 -0
  245. package/dist/presentation/colors.js.map +1 -1
  246. package/dist/presentation/complexity.d.ts.map +1 -1
  247. package/dist/presentation/complexity.js +25 -19
  248. package/dist/presentation/complexity.js.map +1 -1
  249. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  250. package/dist/presentation/queries-cli/exports.js +15 -15
  251. package/dist/presentation/queries-cli/exports.js.map +1 -1
  252. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  253. package/dist/presentation/queries-cli/impact.js +29 -19
  254. package/dist/presentation/queries-cli/impact.js.map +1 -1
  255. package/dist/types.d.ts +182 -7
  256. package/dist/types.d.ts.map +1 -1
  257. package/grammars/tree-sitter-bash.wasm +0 -0
  258. package/grammars/tree-sitter-c.wasm +0 -0
  259. package/grammars/tree-sitter-cpp.wasm +0 -0
  260. package/grammars/tree-sitter-dart.wasm +0 -0
  261. package/grammars/tree-sitter-elixir.wasm +0 -0
  262. package/grammars/tree-sitter-haskell.wasm +0 -0
  263. package/grammars/tree-sitter-kotlin.wasm +0 -0
  264. package/grammars/tree-sitter-lua.wasm +0 -0
  265. package/grammars/tree-sitter-ocaml.wasm +0 -0
  266. package/grammars/tree-sitter-scala.wasm +0 -0
  267. package/grammars/tree-sitter-swift.wasm +0 -0
  268. package/grammars/tree-sitter-zig.wasm +0 -0
  269. package/package.json +19 -7
  270. package/src/ast-analysis/engine.ts +147 -138
  271. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  272. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  273. package/src/db/connection.ts +90 -59
  274. package/src/db/index.ts +1 -0
  275. package/src/db/migrations.ts +36 -32
  276. package/src/domain/analysis/context.ts +73 -75
  277. package/src/domain/analysis/dependencies.ts +78 -68
  278. package/src/domain/analysis/exports.ts +45 -34
  279. package/src/domain/analysis/fn-impact.ts +67 -64
  280. package/src/domain/analysis/module-map.ts +103 -8
  281. package/src/domain/analysis/query-helpers.ts +35 -0
  282. package/src/domain/graph/builder/helpers.ts +12 -6
  283. package/src/domain/graph/builder/incremental.ts +3 -2
  284. package/src/domain/graph/builder/pipeline.ts +71 -3
  285. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  286. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  287. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  288. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  289. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  290. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  291. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  292. package/src/domain/graph/resolve.ts +34 -46
  293. package/src/domain/graph/watcher.ts +12 -14
  294. package/src/domain/parser.ts +222 -97
  295. package/src/domain/search/search/cli-formatter.ts +121 -94
  296. package/src/extractors/bash.ts +97 -0
  297. package/src/extractors/c.ts +212 -0
  298. package/src/extractors/cpp.ts +298 -0
  299. package/src/extractors/csharp.ts +53 -56
  300. package/src/extractors/dart.ts +304 -0
  301. package/src/extractors/elixir.ts +251 -0
  302. package/src/extractors/go.ts +152 -134
  303. package/src/extractors/haskell.ts +235 -0
  304. package/src/extractors/hcl.ts +6 -6
  305. package/src/extractors/helpers.ts +93 -1
  306. package/src/extractors/index.ts +12 -0
  307. package/src/extractors/java.ts +43 -48
  308. package/src/extractors/javascript.ts +328 -281
  309. package/src/extractors/kotlin.ts +293 -0
  310. package/src/extractors/lua.ts +169 -0
  311. package/src/extractors/ocaml.ts +259 -0
  312. package/src/extractors/php.ts +46 -40
  313. package/src/extractors/python.ts +81 -104
  314. package/src/extractors/ruby.ts +6 -13
  315. package/src/extractors/rust.ts +65 -85
  316. package/src/extractors/scala.ts +285 -0
  317. package/src/extractors/swift.ts +293 -0
  318. package/src/extractors/zig.ts +294 -0
  319. package/src/features/ast.ts +10 -25
  320. package/src/features/audit.ts +24 -20
  321. package/src/features/branch-compare.ts +51 -4
  322. package/src/features/cfg.ts +113 -65
  323. package/src/features/check.ts +90 -74
  324. package/src/features/complexity-query.ts +181 -163
  325. package/src/features/complexity.ts +64 -1
  326. package/src/features/dataflow.ts +462 -217
  327. package/src/features/graph-enrichment.ts +161 -117
  328. package/src/features/sequence.ts +27 -4
  329. package/src/features/structure-query.ts +43 -4
  330. package/src/features/structure.ts +50 -22
  331. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  332. package/src/graph/algorithms/leiden/index.ts +67 -28
  333. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  334. package/src/graph/algorithms/leiden/partition.ts +131 -98
  335. package/src/graph/model.ts +19 -7
  336. package/src/infrastructure/config.ts +60 -58
  337. package/src/infrastructure/registry.ts +17 -14
  338. package/src/mcp/server.ts +46 -37
  339. package/src/presentation/audit.ts +72 -67
  340. package/src/presentation/branch-compare.ts +54 -50
  341. package/src/presentation/check.ts +34 -34
  342. package/src/presentation/colors.ts +2 -0
  343. package/src/presentation/complexity.ts +39 -33
  344. package/src/presentation/queries-cli/exports.ts +17 -17
  345. package/src/presentation/queries-cli/impact.ts +30 -22
  346. package/src/types.ts +195 -7
@@ -0,0 +1,35 @@
1
+ import { openReadonlyOrFail } from '../../db/index.js';
2
+ import { loadConfig } from '../../infrastructure/config.js';
3
+ import type { BetterSqlite3Database, CodegraphConfig } from '../../types.js';
4
+
5
+ /**
6
+ * Open a readonly DB connection, run `fn`, and close the DB on completion.
7
+ * Eliminates the duplicated `openReadonlyOrFail` + `try/finally/db.close()` pattern
8
+ * that appears in every analysis query function.
9
+ */
10
+ export function withReadonlyDb<T>(
11
+ customDbPath: string | undefined,
12
+ fn: (db: BetterSqlite3Database) => T,
13
+ ): T {
14
+ const db = openReadonlyOrFail(customDbPath);
15
+ try {
16
+ return fn(db);
17
+ } finally {
18
+ db.close();
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Resolve common analysis options into a normalized form.
24
+ * Shared across fn-impact, context, dependencies, and exports modules.
25
+ */
26
+ export function resolveAnalysisOpts(opts: { noTests?: boolean; config?: CodegraphConfig }): {
27
+ noTests: boolean;
28
+ config: CodegraphConfig;
29
+ displayOpts: Record<string, unknown>;
30
+ } {
31
+ const noTests = opts.noTests || false;
32
+ const config = opts.config || loadConfig();
33
+ const displayOpts = config.display || {};
34
+ return { noTests, config, displayOpts };
35
+ }
@@ -47,6 +47,17 @@ export const BUILTIN_RECEIVERS: Set<string> = new Set([
47
47
  'require',
48
48
  ]);
49
49
 
50
+ /** Check if a directory entry should be skipped (ignored dirs, dotfiles). */
51
+ function shouldSkipEntry(entry: fs.Dirent, extraIgnore: Set<string> | null): boolean {
52
+ if (entry.name.startsWith('.') && entry.name !== '.') {
53
+ if (IGNORE_DIRS.has(entry.name)) return true;
54
+ if (entry.isDirectory()) return true;
55
+ }
56
+ if (IGNORE_DIRS.has(entry.name)) return true;
57
+ if (extraIgnore?.has(entry.name)) return true;
58
+ return false;
59
+ }
60
+
50
61
  /**
51
62
  * Recursively collect all source files under `dir`.
52
63
  * When `directories` is a Set, also tracks which directories contain files.
@@ -100,12 +111,7 @@ export function collectFiles(
100
111
  }
101
112
 
102
113
  for (const entry of entries) {
103
- if (entry.name.startsWith('.') && entry.name !== '.') {
104
- if (IGNORE_DIRS.has(entry.name)) continue;
105
- if (entry.isDirectory()) continue;
106
- }
107
- if (IGNORE_DIRS.has(entry.name)) continue;
108
- if (extraIgnore?.has(entry.name)) continue;
114
+ if (shouldSkipEntry(entry, extraIgnore)) continue;
109
115
 
110
116
  const full = path.join(dir, entry.name);
111
117
  if (entry.isDirectory()) {
@@ -10,7 +10,7 @@
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import { bulkNodeIdsByFile } from '../../../db/index.js';
13
- import { warn } from '../../../infrastructure/logger.js';
13
+ import { debug, warn } from '../../../infrastructure/logger.js';
14
14
  import { normalizePath } from '../../../shared/constants.js';
15
15
  import type {
16
16
  BetterSqlite3Database,
@@ -154,7 +154,8 @@ async function parseReverseDep(
154
154
  let code: string;
155
155
  try {
156
156
  code = readFileSafe(absPath);
157
- } catch {
157
+ } catch (e: unknown) {
158
+ debug(`parseReverseDep: cannot read ${absPath}: ${e instanceof Error ? e.message : String(e)}`);
158
159
  return null;
159
160
  }
160
161
 
@@ -35,6 +35,18 @@ function initializeEngine(ctx: PipelineContext): void {
35
35
  dataflow: ctx.opts.dataflow !== false,
36
36
  ast: ctx.opts.ast !== false,
37
37
  nativeDb: ctx.nativeDb,
38
+ // WAL checkpoint callbacks for dual-connection WAL guard (#696).
39
+ // Feature modules (ast, cfg, complexity, dataflow) receive `db` as a
40
+ // parameter and cannot tolerate close/reopen (stale reference). Instead,
41
+ // checkpoint the WAL so native writes start with a clean slate. Features
42
+ // return early on native success and never read native-written WAL data
43
+ // through the JS connection, so a post-write checkpoint is unnecessary.
44
+ suspendJsDb: ctx.nativeDb
45
+ ? () => {
46
+ ctx.db.pragma('wal_checkpoint(TRUNCATE)');
47
+ }
48
+ : undefined,
49
+ resumeJsDb: ctx.nativeDb ? () => {} : undefined,
38
50
  };
39
51
  const { name: engineName, version: engineVersion } = getActiveEngine(ctx.engineOpts);
40
52
  ctx.engineName = engineName as 'native' | 'wasm';
@@ -48,9 +60,11 @@ function checkEngineSchemaMismatch(ctx: PipelineContext): void {
48
60
  ctx.forceFullRebuild = false;
49
61
  if (!ctx.incremental) return;
50
62
 
51
- // Route metadata reads through NativeDatabase when available (Phase 6.13)
63
+ // Route metadata reads through NativeDatabase only when using the native engine,
64
+ // to avoid dual-SQLite WAL conflicts (rusqlite + better-sqlite3 on same file).
65
+ const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
52
66
  const meta = (key: string): string | null =>
53
- ctx.nativeDb ? ctx.nativeDb.getBuildMeta(key) : getBuildMeta(ctx.db, key);
67
+ useNativeDb ? ctx.nativeDb!.getBuildMeta(key) : getBuildMeta(ctx.db, key);
54
68
 
55
69
  const prevEngine = meta('engine');
56
70
  if (prevEngine && prevEngine !== ctx.engineName) {
@@ -109,8 +123,10 @@ function setupPipeline(ctx: PipelineContext): void {
109
123
  } catch (err) {
110
124
  warn(`NativeDatabase init failed, falling back to JS: ${(err as Error).message}`);
111
125
  ctx.nativeDb = undefined;
112
- initSchema(ctx.db);
113
126
  }
127
+ // Always run JS initSchema so better-sqlite3 sees the schema —
128
+ // nativeDb is closed during pipeline stages and reopened for analyses.
129
+ initSchema(ctx.db);
114
130
  } else {
115
131
  initSchema(ctx.db);
116
132
  }
@@ -156,6 +172,26 @@ function formatTimingResult(ctx: PipelineContext): BuildResult {
156
172
  // ── Pipeline stages execution ───────────────────────────────────────────
157
173
 
158
174
  async function runPipelineStages(ctx: PipelineContext): Promise<void> {
175
+ // Prevent dual-connection WAL corruption during pipeline stages: when both
176
+ // better-sqlite3 (ctx.db) and rusqlite (ctx.nativeDb) are open to the same
177
+ // WAL-mode file, native writes corrupt the DB. Close nativeDb so stages
178
+ // use JS fallback paths. Reopened before runAnalyses for feature modules
179
+ // that use suspendJsDb/resumeJsDb WAL checkpoint pattern (#696).
180
+ const hadNativeDb = !!ctx.nativeDb;
181
+ if (ctx.db && ctx.nativeDb) {
182
+ try {
183
+ ctx.nativeDb.close();
184
+ } catch {
185
+ /* ignore close errors */
186
+ }
187
+ ctx.nativeDb = undefined;
188
+ // Also clear stale reference in engineOpts to prevent stages from
189
+ // calling methods on the closed NativeDatabase.
190
+ if (ctx.engineOpts?.nativeDb) {
191
+ ctx.engineOpts.nativeDb = undefined;
192
+ }
193
+ }
194
+
159
195
  await collectFiles(ctx);
160
196
  await detectChanges(ctx);
161
197
 
@@ -166,7 +202,39 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
166
202
  await resolveImports(ctx);
167
203
  await buildEdges(ctx);
168
204
  await buildStructure(ctx);
205
+
206
+ // Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow)
207
+ // which use suspendJsDb/resumeJsDb WAL checkpoint before native writes.
208
+ if (hadNativeDb) {
209
+ const native = loadNative();
210
+ if (native?.NativeDatabase) {
211
+ try {
212
+ ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
213
+ if (ctx.engineOpts) {
214
+ ctx.engineOpts.nativeDb = ctx.nativeDb;
215
+ }
216
+ } catch {
217
+ ctx.nativeDb = undefined;
218
+ if (ctx.engineOpts) {
219
+ ctx.engineOpts.nativeDb = undefined;
220
+ }
221
+ }
222
+ }
223
+ }
224
+
169
225
  await runAnalyses(ctx);
226
+
227
+ // Close nativeDb after analyses — finalize uses JS paths for setBuildMeta
228
+ // and closeDbPair handles cleanup. Avoids dual-connection during finalize.
229
+ if (ctx.nativeDb) {
230
+ try {
231
+ ctx.nativeDb.close();
232
+ } catch {
233
+ /* ignore close errors */
234
+ }
235
+ ctx.nativeDb = undefined;
236
+ }
237
+
170
238
  await finalize(ctx);
171
239
  }
172
240
 
@@ -6,9 +6,9 @@
6
6
  */
7
7
  import path from 'node:path';
8
8
  import { performance } from 'node:perf_hooks';
9
- import { getNodeId } from '#db/index.js';
10
- import { debug } from '#infrastructure/logger.js';
11
- import { loadNative } from '#infrastructure/native.js';
9
+ import { getNodeId } from '../../../../db/index.js';
10
+ import { debug } from '../../../../infrastructure/logger.js';
11
+ import { loadNative } from '../../../../infrastructure/native.js';
12
12
  import type {
13
13
  BetterSqlite3Database,
14
14
  Call,
@@ -18,10 +18,11 @@ import type {
18
18
  NativeAddon,
19
19
  NodeRow,
20
20
  TypeMapEntry,
21
- } from '#types';
21
+ } from '../../../../types.js';
22
22
  import { computeConfidence } from '../../resolve.js';
23
23
  import type { PipelineContext } from '../context.js';
24
24
  import { BUILTIN_RECEIVERS, batchInsertEdges } from '../helpers.js';
25
+
25
26
  import { getResolved, isBarrelFile, resolveBarrelExport } from './resolve-imports.js';
26
27
 
27
28
  // ── Local types ──────────────────────────────────────────────────────────
@@ -61,12 +62,6 @@ interface NativeEdge {
61
62
  dynamic: number;
62
63
  }
63
64
 
64
- /** TypeMap entry used in receiver supplement (normalized from native format). */
65
- interface NormalizedTypeEntry {
66
- type: string;
67
- confidence: number;
68
- }
69
-
70
65
  // ── Node lookup setup ───────────────────────────────────────────────────
71
66
 
72
67
  function makeGetNodeIdStmt(db: BetterSqlite3Database): NodeIdStmt {
@@ -210,14 +205,6 @@ function buildCallEdgesNative(
210
205
  for (const e of nativeEdges) {
211
206
  allEdgeRows.push([e.sourceId, e.targetId, e.kind, e.confidence, e.dynamic]);
212
207
  }
213
-
214
- // Older native binaries (< 3.2.0) don't emit receiver or type-resolved method-call
215
- // edges. Supplement them on the JS side if the native binary missed them.
216
- // TODO: Remove once all published native binaries handle receivers (>= 3.2.0)
217
- const hasReceiver = nativeEdges.some((e) => e.kind === 'receiver');
218
- if (!hasReceiver) {
219
- supplementReceiverEdges(ctx, nativeFiles, getNodeIdStmt, allEdgeRows);
220
- }
221
208
  }
222
209
 
223
210
  function buildImportedNamesForNative(
@@ -242,58 +229,6 @@ function buildImportedNamesForNative(
242
229
  return importedNames;
243
230
  }
244
231
 
245
- // ── Receiver edge supplement for older native binaries ──────────────────
246
-
247
- function supplementReceiverEdges(
248
- ctx: PipelineContext,
249
- nativeFiles: NativeFileEntry[],
250
- getNodeIdStmt: NodeIdStmt,
251
- allEdgeRows: EdgeRowTuple[],
252
- ): void {
253
- const seenCallEdges = new Set<string>();
254
- // Collect existing edges to avoid duplicates
255
- for (const row of allEdgeRows) {
256
- seenCallEdges.add(`${row[0]}|${row[1]}|${row[2]}`);
257
- }
258
-
259
- for (const nf of nativeFiles) {
260
- const relPath = nf.file;
261
- const typeMap = new Map<string, NormalizedTypeEntry>(
262
- nf.typeMap.map((t) => [t.name, { type: t.typeName, confidence: t.confidence ?? 0.9 }]),
263
- );
264
- const fileNodeRow = { id: nf.fileNodeId };
265
-
266
- for (const call of nf.calls) {
267
- if (!call.receiver || BUILTIN_RECEIVERS.has(call.receiver)) continue;
268
- if (call.receiver === 'this' || call.receiver === 'self' || call.receiver === 'super')
269
- continue;
270
-
271
- const caller = findCaller(call, nf.definitions, relPath, getNodeIdStmt, fileNodeRow);
272
-
273
- // Receiver edge: caller → receiver type node
274
- buildReceiverEdge(ctx, call, caller, relPath, seenCallEdges, allEdgeRows, typeMap);
275
-
276
- // Type-resolved method call: caller → Type.method
277
- const typeEntry = typeMap.get(call.receiver);
278
- const typeName = typeEntry ? typeEntry.type : null;
279
- if (typeName) {
280
- const qualifiedName = `${typeName}.${call.name}`;
281
- const targets = (ctx.nodesByName.get(qualifiedName) || []).filter(
282
- (n) => n.kind === 'method',
283
- );
284
- for (const t of targets) {
285
- const key = `${caller.id}|${t.id}|calls`;
286
- if (t.id !== caller.id && !seenCallEdges.has(key)) {
287
- seenCallEdges.add(key);
288
- const confidence = computeConfidence(relPath, t.file, null);
289
- allEdgeRows.push([caller.id, t.id, 'calls', confidence, call.dynamic ? 1 : 0]);
290
- }
291
- }
292
- }
293
- }
294
- }
295
- }
296
-
297
232
  // ── Call edges (JS fallback) ────────────────────────────────────────────
298
233
 
299
234
  function buildCallEdgesJS(
@@ -495,7 +430,7 @@ function buildReceiverEdge(
495
430
  relPath: string,
496
431
  seenCallEdges: Set<string>,
497
432
  allEdgeRows: EdgeRowTuple[],
498
- typeMap: Map<string, TypeMapEntry | NormalizedTypeEntry | string>,
433
+ typeMap: Map<string, TypeMapEntry | string>,
499
434
  ): void {
500
435
  const receiverKinds = new Set(['class', 'struct', 'interface', 'type', 'module']);
501
436
  const typeEntry = typeMap?.get(call.receiver!);
@@ -608,7 +543,7 @@ function loadNodes(ctx: PipelineContext): { rows: QueryNodeRow[]; scoped: boolea
608
543
 
609
544
  /**
610
545
  * For scoped node loading, patch nodesByName.get with a lazy SQL fallback
611
- * so global name-only lookups (resolveByMethodOrGlobal, supplementReceiverEdges)
546
+ * so global name-only lookups (resolveByMethodOrGlobal)
612
547
  * can still find nodes outside the scoped set.
613
548
  */
614
549
  function addLazyFallback(ctx: PipelineContext, scopedLoad: boolean): void {
@@ -673,7 +608,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
673
608
 
674
609
  // When using native edge insert, skip JS insert here — do it after tx commits.
675
610
  // Otherwise insert edges within this transaction for atomicity.
676
- const useNativeEdgeInsert = !!ctx.nativeDb?.bulkInsertEdges;
611
+ const useNativeEdgeInsert = ctx.engineName === 'native' && !!ctx.nativeDb?.bulkInsertEdges;
677
612
  if (!useNativeEdgeInsert) {
678
613
  batchInsertEdges(db, allEdgeRows);
679
614
  }
@@ -683,7 +618,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
683
618
  // Phase 2: Native rusqlite bulk insert (outside better-sqlite3 transaction
684
619
  // to avoid SQLITE_BUSY contention). Uses NativeDatabase persistent connection.
685
620
  // Standalone napi functions were removed in 6.17.
686
- if (ctx.nativeDb?.bulkInsertEdges && allEdgeRows.length > 0) {
621
+ if (ctx.engineName === 'native' && ctx.nativeDb?.bulkInsertEdges && allEdgeRows.length > 0) {
687
622
  const nativeEdges = allEdgeRows.map((r) => ({
688
623
  sourceId: r[0],
689
624
  targetId: r[1],
@@ -694,7 +629,7 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
694
629
  const ok = ctx.nativeDb.bulkInsertEdges(nativeEdges);
695
630
  if (!ok) {
696
631
  debug('Native bulkInsertEdges failed — falling back to JS batchInsertEdges');
697
- batchInsertEdges(db, allEdgeRows);
632
+ batchInsertEdges(ctx.db, allEdgeRows);
698
633
  }
699
634
  }
700
635
 
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import path from 'node:path';
7
7
  import { performance } from 'node:perf_hooks';
8
- import { debug } from '#infrastructure/logger.js';
9
- import { normalizePath } from '#shared/constants.js';
10
- import type { ExtractorOutput } from '#types';
8
+ import { debug } from '../../../../infrastructure/logger.js';
9
+ import { normalizePath } from '../../../../shared/constants.js';
10
+ import type { ExtractorOutput } from '../../../../types.js';
11
11
  import type { PipelineContext } from '../context.js';
12
12
  import { readFileSafe } from '../helpers.js';
13
13
 
@@ -39,10 +39,11 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
39
39
  // loading ALL definitions from DB (~8ms) and recomputing ALL metrics (~15ms).
40
40
  // Gate: ≤5 changed files AND significantly more existing files (>20) to
41
41
  // avoid triggering on small test fixtures where directory metrics matter.
42
+ const useNativeReads = ctx.engineName === 'native' && !!ctx.nativeDb;
42
43
  const existingFileCount = !isFullBuild
43
44
  ? (
44
- (ctx.nativeDb
45
- ? ctx.nativeDb.queryGet("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'", [])
45
+ (useNativeReads
46
+ ? ctx.nativeDb!.queryGet("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'", [])
46
47
  : db.prepare("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'").get()) as {
47
48
  c: number;
48
49
  }
@@ -96,7 +97,8 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
96
97
 
97
98
  // Use NativeDatabase persistent connection (Phase 6.15+).
98
99
  // Standalone napi functions were removed in 6.17 — falls through to JS if nativeDb unavailable.
99
- if (ctx.nativeDb?.classifyRolesFull) {
100
+ // Note: classifyRoles* both read (fan-in/fan-out) and write (UPDATE nodes SET role).
101
+ if (useNativeReads && ctx.nativeDb?.classifyRolesFull) {
100
102
  const nativeResult =
101
103
  changedFileList && changedFileList.length > 0
102
104
  ? ctx.nativeDb.classifyRolesIncremental(changedFileList)
@@ -126,7 +128,7 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
126
128
  changedFiles?: string[] | null,
127
129
  ) => Record<string, number>;
128
130
  };
129
- roleSummary = classifyNodeRoles(db, changedFileList);
131
+ roleSummary = classifyNodeRoles(ctx.db, changedFileList);
130
132
  }
131
133
 
132
134
  debug(
@@ -7,8 +7,8 @@
7
7
  */
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
- import { debug, info } from '#infrastructure/logger.js';
11
- import { normalizePath } from '#shared/constants.js';
10
+ import { debug, info } from '../../../../infrastructure/logger.js';
11
+ import { normalizePath } from '../../../../shared/constants.js';
12
12
  import { readJournal } from '../../journal.js';
13
13
  import type { PipelineContext } from '../context.js';
14
14
  import { collectFiles as collectFilesUtil } from '../helpers.js';
@@ -338,7 +338,7 @@ function purgeAndAddReverseDeps(
338
338
  if (hasPurge || hasReverseDeps) {
339
339
  const filesToPurge = hasPurge ? [...ctx.removed, ...changePaths] : [];
340
340
  // Prefer NativeDatabase: purge + reverse-dep edge deletion in one transaction (#670)
341
- if (ctx.nativeDb?.purgeFilesData) {
341
+ if (ctx.engineName === 'native' && ctx.nativeDb?.purgeFilesData) {
342
342
  ctx.nativeDb.purgeFilesData(filesToPurge, false, hasReverseDeps ? reverseDepList : undefined);
343
343
  } else {
344
344
  if (hasPurge) {
@@ -433,7 +433,12 @@ export async function detectChanges(ctx: PipelineContext): Promise<void> {
433
433
  }
434
434
  const increResult =
435
435
  incremental && !forceFullRebuild
436
- ? getChangedFiles(db, allFiles, rootDir, ctx.nativeDb)
436
+ ? getChangedFiles(
437
+ db,
438
+ allFiles,
439
+ rootDir,
440
+ ctx.engineName === 'native' ? ctx.nativeDb : undefined,
441
+ )
437
442
  : {
438
443
  changed: allFiles.map((f): ChangedFile => ({ file: f })),
439
444
  removed: [] as string[],