@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
@@ -3,6 +3,10 @@
3
3
  *
4
4
  * Batch-inserts file nodes, definitions, exports, children, and contains/parameter_of edges.
5
5
  * Updates file hashes for incremental builds.
6
+ *
7
+ * When the native engine is available, delegates all SQLite writes to Rust via
8
+ * `bulkInsertNodes` — eliminating JS↔C boundary overhead. Falls back to the
9
+ * JS implementation on failure or when native is unavailable.
6
10
  */
7
11
  import path from 'node:path';
8
12
  import { performance } from 'node:perf_hooks';
@@ -32,7 +36,118 @@ interface PrecomputedFileData {
32
36
  _reverseDepOnly?: boolean;
33
37
  }
34
38
 
35
- // ── Phase 1: Insert file nodes, definitions, exports ────────────────────
39
+ // ── Native fast-path ─────────────────────────────────────────────────
40
+
41
+ function tryNativeInsert(ctx: PipelineContext): boolean {
42
+ // Disabled: bulkInsertNodes corrupts the DB when both the JS (better-sqlite3)
43
+ // and Rust (rusqlite) connections are open to the same WAL-mode file.
44
+ // The native path was never operational before — it always crashed on null
45
+ // visibility serialisation. See #696 for the dual-connection fix.
46
+ if (ctx.db) return false;
47
+
48
+ // Use NativeDatabase persistent connection (Phase 6.15+).
49
+ // Standalone napi functions were removed in 6.17 — falls through to JS if nativeDb unavailable.
50
+ if (!ctx.nativeDb?.bulkInsertNodes) return false;
51
+
52
+ const { allSymbols, filesToParse, metadataUpdates, rootDir, removed } = ctx;
53
+
54
+ // Marshal allSymbols → InsertNodesBatch[]
55
+ const batches: Array<{
56
+ file: string;
57
+ definitions: Array<{
58
+ name: string;
59
+ kind: string;
60
+ line: number;
61
+ endLine?: number;
62
+ visibility?: string;
63
+ children: Array<{
64
+ name: string;
65
+ kind: string;
66
+ line: number;
67
+ endLine?: number;
68
+ visibility?: string;
69
+ }>;
70
+ }>;
71
+ exports: Array<{ name: string; kind: string; line: number }>;
72
+ }> = [];
73
+
74
+ for (const [relPath, symbols] of allSymbols) {
75
+ batches.push({
76
+ file: relPath,
77
+ definitions: symbols.definitions.map((def) => ({
78
+ name: def.name,
79
+ kind: def.kind,
80
+ line: def.line,
81
+ endLine: def.endLine ?? undefined,
82
+ visibility: def.visibility ?? undefined,
83
+ children: (def.children ?? []).map((c) => ({
84
+ name: c.name,
85
+ kind: c.kind,
86
+ line: c.line,
87
+ endLine: c.endLine ?? undefined,
88
+ visibility: c.visibility ?? undefined,
89
+ })),
90
+ })),
91
+ exports: symbols.exports.map((exp) => ({
92
+ name: exp.name,
93
+ kind: exp.kind,
94
+ line: exp.line,
95
+ })),
96
+ });
97
+ }
98
+
99
+ // Build file hash entries
100
+ const precomputedData = new Map<string, PrecomputedFileData>();
101
+ for (const item of filesToParse) {
102
+ if (item.relPath) precomputedData.set(item.relPath, item as PrecomputedFileData);
103
+ }
104
+
105
+ const fileHashes: Array<{ file: string; hash: string; mtime: number; size: number }> = [];
106
+ for (const [relPath] of allSymbols) {
107
+ const precomputed = precomputedData.get(relPath);
108
+ if (precomputed?._reverseDepOnly) {
109
+ continue; // file unchanged, hash already correct
110
+ }
111
+ if (precomputed?.hash) {
112
+ let mtime: number;
113
+ let size: number;
114
+ if (precomputed.stat) {
115
+ mtime = precomputed.stat.mtime;
116
+ size = precomputed.stat.size;
117
+ } else {
118
+ const rawStat = fileStat(path.join(rootDir, relPath));
119
+ mtime = rawStat ? Math.floor(rawStat.mtimeMs) : 0;
120
+ size = rawStat ? rawStat.size : 0;
121
+ }
122
+ fileHashes.push({ file: relPath, hash: precomputed.hash, mtime, size });
123
+ } else {
124
+ const absPath = path.join(rootDir, relPath);
125
+ let code: string | null;
126
+ try {
127
+ code = readFileSafe(absPath);
128
+ } catch {
129
+ code = null;
130
+ }
131
+ if (code !== null) {
132
+ const stat = fileStat(absPath);
133
+ const mtime = stat ? Math.floor(stat.mtimeMs) : 0;
134
+ const size = stat ? stat.size : 0;
135
+ fileHashes.push({ file: relPath, hash: fileHash(code), mtime, size });
136
+ }
137
+ }
138
+ }
139
+
140
+ // Also include metadata-only updates (self-heal mtime/size without re-parse)
141
+ for (const item of metadataUpdates) {
142
+ const mtime = item.stat ? Math.floor(item.stat.mtime) : 0;
143
+ const size = item.stat ? item.stat.size : 0;
144
+ fileHashes.push({ file: item.relPath, hash: item.hash, mtime, size });
145
+ }
146
+
147
+ return ctx.nativeDb!.bulkInsertNodes(batches, fileHashes, removed);
148
+ }
149
+
150
+ // ── JS fallback: Phase 1 ────────────────────────────────────────────
36
151
 
37
152
  function insertDefinitionsAndExports(
38
153
  db: BetterSqlite3Database,
@@ -90,7 +205,7 @@ function insertDefinitionsAndExports(
90
205
  }
91
206
  }
92
207
 
93
- // ── Phase 2+3: Insert children and containment edges (two nodeIdMap passes) ──
208
+ // ── JS fallback: Phase 2+3 ──────────────────────────────────────────
94
209
 
95
210
  function insertChildrenAndEdges(
96
211
  db: BetterSqlite3Database,
@@ -165,7 +280,7 @@ function insertChildrenAndEdges(
165
280
  batchInsertEdges(db, edgeRows);
166
281
  }
167
282
 
168
- // ── Phase 4: Update file hashes ─────────────────────────────────────────
283
+ // ── JS fallback: Phase 4 ────────────────────────────────────────────
169
284
 
170
285
  function updateFileHashes(
171
286
  _db: BetterSqlite3Database,
@@ -218,11 +333,32 @@ function updateFileHashes(
218
333
  }
219
334
  }
220
335
 
221
- // ── Main entry point ────────────────────────────────────────────────────
336
+ // ── Main entry point ────────────────────────────────────────────────
222
337
 
223
338
  export async function insertNodes(ctx: PipelineContext): Promise<void> {
224
- const { db, allSymbols, filesToParse, metadataUpdates, rootDir, removed } = ctx;
339
+ const { allSymbols, filesToParse, metadataUpdates, rootDir, removed } = ctx;
340
+
341
+ // Populate fileSymbols before any DB writes (used by later stages)
342
+ for (const [relPath, symbols] of allSymbols) {
343
+ ctx.fileSymbols.set(relPath, symbols);
344
+ }
345
+
346
+ const t0 = performance.now();
347
+
348
+ // Try native Rust path first — single transaction, no JS↔C overhead
349
+ if (ctx.engineName === 'native') {
350
+ try {
351
+ if (tryNativeInsert(ctx)) {
352
+ ctx.timing.insertMs = performance.now() - t0;
353
+ // Removed-file hash cleanup is handled inside the native call
354
+ return;
355
+ }
356
+ } catch {
357
+ // Native insert failed — fall through to JS implementation
358
+ }
359
+ }
225
360
 
361
+ // JS fallback
226
362
  const precomputedData = new Map<string, PrecomputedFileData>();
227
363
  for (const item of filesToParse) {
228
364
  if (item.relPath) precomputedData.set(item.relPath, item as PrecomputedFileData);
@@ -230,31 +366,25 @@ export async function insertNodes(ctx: PipelineContext): Promise<void> {
230
366
 
231
367
  let upsertHash: SqliteStatement | null;
232
368
  try {
233
- upsertHash = db.prepare(
369
+ upsertHash = ctx.db.prepare(
234
370
  'INSERT OR REPLACE INTO file_hashes (file, hash, mtime, size) VALUES (?, ?, ?, ?)',
235
371
  );
236
372
  } catch {
237
373
  upsertHash = null;
238
374
  }
239
375
 
240
- // Populate fileSymbols before the transaction so it is a pure input
241
- for (const [relPath, symbols] of allSymbols) {
242
- ctx.fileSymbols.set(relPath, symbols);
243
- }
244
-
245
- const insertAll = db.transaction(() => {
246
- insertDefinitionsAndExports(db, allSymbols);
247
- insertChildrenAndEdges(db, allSymbols);
248
- updateFileHashes(db, allSymbols, precomputedData, metadataUpdates, rootDir, upsertHash);
376
+ const insertAll = ctx.db.transaction(() => {
377
+ insertDefinitionsAndExports(ctx.db, allSymbols);
378
+ insertChildrenAndEdges(ctx.db, allSymbols);
379
+ updateFileHashes(ctx.db, allSymbols, precomputedData, metadataUpdates, rootDir, upsertHash);
249
380
  });
250
381
 
251
- const t0 = performance.now();
252
382
  insertAll();
253
383
  ctx.timing.insertMs = performance.now() - t0;
254
384
 
255
385
  // Clean up removed file hashes
256
386
  if (upsertHash && removed.length > 0) {
257
- const deleteHash = db.prepare('DELETE FROM file_hashes WHERE file = ?');
387
+ const deleteHash = ctx.db.prepare('DELETE FROM file_hashes WHERE file = ?');
258
388
  for (const relPath of removed) {
259
389
  deleteHash.run(relPath);
260
390
  }
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { performance } from 'node:perf_hooks';
3
+ import { debug } from '../../../../infrastructure/logger.js';
3
4
  import type { Import } from '../../../../types.js';
4
5
  import { parseFilesAuto } from '../../../parser.js';
5
6
  import { resolveImportPath, resolveImportsBatch } from '../../resolve.js';
@@ -132,8 +133,8 @@ export async function resolveImports(ctx: PipelineContext): Promise<void> {
132
133
  );
133
134
  }
134
135
  }
135
- } catch {
136
- /* skip if unreadable */
136
+ } catch (e: unknown) {
137
+ debug(`Barrel re-parse failed (non-fatal): ${(e as Error).message}`);
137
138
  }
138
139
  }
139
140
  }
@@ -117,6 +117,35 @@ function matchSubpathPattern(pattern: string, subpath: string): string | null {
117
117
  * Resolve a bare specifier through the package.json exports field.
118
118
  * Returns an absolute path or null.
119
119
  */
120
+ /** Try to resolve a condition target to a file path in packageDir. */
121
+ function tryResolveTarget(target: string | null, packageDir: string): string | null {
122
+ if (!target) return null;
123
+ const resolved = path.resolve(packageDir, target);
124
+ return fs.existsSync(resolved) ? resolved : null;
125
+ }
126
+
127
+ /** Resolve subpath against a subpath map (object with "." keys). */
128
+ function resolveSubpathMap(
129
+ exports: Record<string, unknown>,
130
+ subpath: string,
131
+ packageDir: string,
132
+ ): string | null {
133
+ // Exact match first
134
+ if (subpath in exports) {
135
+ return tryResolveTarget(resolveCondition(exports[subpath]), packageDir);
136
+ }
137
+ // Pattern matching (keys with *)
138
+ for (const [pattern, value] of Object.entries(exports)) {
139
+ if (!pattern.includes('*')) continue;
140
+ const matched = matchSubpathPattern(pattern, subpath);
141
+ if (matched == null) continue;
142
+ const rawTarget = resolveCondition(value);
143
+ if (!rawTarget) continue;
144
+ return tryResolveTarget(rawTarget.replace(/\*/g, matched), packageDir);
145
+ }
146
+ return null;
147
+ }
148
+
120
149
  export function resolveViaExports(specifier: string, rootDir: string): string | null {
121
150
  const parsed = parseBareSpecifier(specifier);
122
151
  if (!parsed) return null;
@@ -131,66 +160,25 @@ export function resolveViaExports(specifier: string, rootDir: string): string |
131
160
 
132
161
  // Simple string exports: "exports": "./index.js"
133
162
  if (typeof exports === 'string') {
134
- if (subpath === '.') {
135
- const resolved = path.resolve(packageDir, exports);
136
- return fs.existsSync(resolved) ? resolved : null;
137
- }
138
- return null;
163
+ return subpath === '.' ? tryResolveTarget(exports, packageDir) : null;
139
164
  }
140
165
 
141
166
  // Array form at top level
142
167
  if (Array.isArray(exports)) {
143
- if (subpath === '.') {
144
- const target = resolveCondition(exports);
145
- if (target) {
146
- const resolved = path.resolve(packageDir, target);
147
- return fs.existsSync(resolved) ? resolved : null;
148
- }
149
- }
150
- return null;
168
+ return subpath === '.' ? tryResolveTarget(resolveCondition(exports), packageDir) : null;
151
169
  }
152
170
 
153
171
  if (typeof exports !== 'object') return null;
154
172
 
155
- // Determine if exports is a conditions object (no keys start with ".")
156
- // or a subpath map (keys start with ".")
173
+ // Determine if exports is a conditions object or a subpath map
157
174
  const keys = Object.keys(exports);
158
175
  const isSubpathMap = keys.length > 0 && keys.some((k) => k.startsWith('.'));
159
176
 
160
177
  if (!isSubpathMap) {
161
- // Conditions object at top level applies to "." subpath only
162
- if (subpath === '.') {
163
- const target = resolveCondition(exports);
164
- if (target) {
165
- const resolved = path.resolve(packageDir, target);
166
- return fs.existsSync(resolved) ? resolved : null;
167
- }
168
- }
169
- return null;
178
+ return subpath === '.' ? tryResolveTarget(resolveCondition(exports), packageDir) : null;
170
179
  }
171
180
 
172
- // Subpath map: try exact match first, then pattern match
173
- if (subpath in exports) {
174
- const target = resolveCondition(exports[subpath]);
175
- if (target) {
176
- const resolved = path.resolve(packageDir, target);
177
- return fs.existsSync(resolved) ? resolved : null;
178
- }
179
- }
180
-
181
- // Pattern matching (keys with *)
182
- for (const [pattern, value] of Object.entries(exports)) {
183
- if (!pattern.includes('*')) continue;
184
- const matched = matchSubpathPattern(pattern, subpath);
185
- if (matched == null) continue;
186
- const rawTarget = resolveCondition(value);
187
- if (!rawTarget) continue;
188
- const target = rawTarget.replace(/\*/g, matched);
189
- const resolved = path.resolve(packageDir, target);
190
- if (fs.existsSync(resolved)) return resolved;
191
- }
192
-
193
- return null;
181
+ return resolveSubpathMap(exports as Record<string, unknown>, subpath, packageDir);
194
182
  }
195
183
 
196
184
  /** Clear the exports cache (for testing). */
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { closeDb, getNodeId as getNodeIdQuery, initSchema, openDb } from '../../db/index.js';
4
- import { info } from '../../infrastructure/logger.js';
4
+ import { debug, info } from '../../infrastructure/logger.js';
5
5
  import { EXTENSIONS, IGNORE_DIRS, normalizePath } from '../../shared/constants.js';
6
6
  import { DbError } from '../../shared/errors.js';
7
7
  import { createParseTreeCache, getActiveEngine } from '../parser.js';
@@ -32,12 +32,10 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
32
32
  ast: false,
33
33
  };
34
34
  const { name: engineName, version: engineVersion } = getActiveEngine(engineOpts);
35
- console.log(
36
- `Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`,
37
- );
35
+ info(`Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`);
38
36
 
39
37
  const cache = createParseTreeCache();
40
- console.log(
38
+ info(
41
39
  cache
42
40
  ? 'Incremental parsing enabled (native tree cache)'
43
41
  : 'Incremental parsing unavailable (full re-parse)',
@@ -124,8 +122,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
124
122
  }));
125
123
  try {
126
124
  appendJournalEntries(rootDir, entries);
127
- } catch {
128
- /* journal write failure is non-fatal */
125
+ } catch (e: unknown) {
126
+ debug(`Journal write failed (non-fatal): ${(e as Error).message}`);
129
127
  }
130
128
 
131
129
  const changeEvents = updates.map((r) =>
@@ -137,8 +135,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
137
135
  );
138
136
  try {
139
137
  appendChangeEvents(rootDir, changeEvents);
140
- } catch {
141
- /* change event write failure is non-fatal */
138
+ } catch (e: unknown) {
139
+ debug(`Change event write failed (non-fatal): ${(e as Error).message}`);
142
140
  }
143
141
  }
144
142
 
@@ -153,8 +151,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
153
151
  }
154
152
  }
155
153
 
156
- console.log(`Watching ${rootDir} for changes...`);
157
- console.log('Press Ctrl+C to stop.\n');
154
+ info(`Watching ${rootDir} for changes...`);
155
+ info('Press Ctrl+C to stop.');
158
156
 
159
157
  const watcher = fs.watch(rootDir, { recursive: true }, (_eventType, filename) => {
160
158
  if (!filename) return;
@@ -169,7 +167,7 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
169
167
  });
170
168
 
171
169
  process.on('SIGINT', () => {
172
- console.log('\nStopping watcher...');
170
+ info('Stopping watcher...');
173
171
  watcher.close();
174
172
  // Flush any pending file paths to journal before exit
175
173
  if (pending.size > 0) {
@@ -178,8 +176,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
178
176
  }));
179
177
  try {
180
178
  appendJournalEntries(rootDir, entries);
181
- } catch {
182
- /* best-effort */
179
+ } catch (e: unknown) {
180
+ debug(`Journal flush on exit failed (non-fatal): ${(e as Error).message}`);
183
181
  }
184
182
  }
185
183
  if (cache) cache.clear();