@optave/codegraph 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -16
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +195 -30
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
- package/dist/ast-analysis/rules/javascript.js +0 -1
- package/dist/ast-analysis/rules/javascript.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +52 -129
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/ast.js +2 -2
- package/dist/cli/commands/ast.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +10 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +17 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +6 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +77 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +3 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +26 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +53 -52
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +315 -43
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +106 -1
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +17 -5
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +99 -51
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +34 -7
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +50 -26
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +1 -6
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +101 -32
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +18 -5
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/clojure.d.ts +12 -0
- package/dist/extractors/clojure.d.ts.map +1 -0
- package/dist/extractors/clojure.js +245 -0
- package/dist/extractors/clojure.js.map +1 -0
- package/dist/extractors/cuda.d.ts +11 -0
- package/dist/extractors/cuda.d.ts.map +1 -0
- package/dist/extractors/cuda.js +302 -0
- package/dist/extractors/cuda.js.map +1 -0
- package/dist/extractors/erlang.d.ts +14 -0
- package/dist/extractors/erlang.d.ts.map +1 -0
- package/dist/extractors/erlang.js +239 -0
- package/dist/extractors/erlang.js.map +1 -0
- package/dist/extractors/fsharp.d.ts +13 -0
- package/dist/extractors/fsharp.d.ts.map +1 -0
- package/dist/extractors/fsharp.js +218 -0
- package/dist/extractors/fsharp.js.map +1 -0
- package/dist/extractors/gleam.d.ts +14 -0
- package/dist/extractors/gleam.d.ts.map +1 -0
- package/dist/extractors/gleam.js +229 -0
- package/dist/extractors/gleam.js.map +1 -0
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/groovy.d.ts +10 -0
- package/dist/extractors/groovy.d.ts.map +1 -0
- package/dist/extractors/groovy.js +304 -0
- package/dist/extractors/groovy.js.map +1 -0
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/index.d.ts +11 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +11 -0
- package/dist/extractors/index.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +46 -45
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/julia.d.ts +16 -0
- package/dist/extractors/julia.d.ts.map +1 -0
- package/dist/extractors/julia.js +287 -0
- package/dist/extractors/julia.js.map +1 -0
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.d.ts +9 -0
- package/dist/extractors/objc.d.ts.map +1 -0
- package/dist/extractors/objc.js +406 -0
- package/dist/extractors/objc.js.map +1 -0
- package/dist/extractors/ocaml.js +74 -0
- package/dist/extractors/ocaml.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/r.d.ts +13 -0
- package/dist/extractors/r.d.ts.map +1 -0
- package/dist/extractors/r.js +251 -0
- package/dist/extractors/r.js.map +1 -0
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.d.ts +9 -0
- package/dist/extractors/solidity.d.ts.map +1 -0
- package/dist/extractors/solidity.js +365 -0
- package/dist/extractors/solidity.js.map +1 -0
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/verilog.d.ts +9 -0
- package/dist/extractors/verilog.d.ts.map +1 -0
- package/dist/extractors/verilog.js +286 -0
- package/dist/extractors/verilog.js.map +1 -0
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +84 -83
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/bfs.d.ts +2 -0
- package/dist/graph/algorithms/bfs.d.ts.map +1 -1
- package/dist/graph/algorithms/bfs.js +27 -0
- package/dist/graph/algorithms/bfs.js.map +1 -1
- package/dist/graph/algorithms/centrality.d.ts +2 -0
- package/dist/graph/algorithms/centrality.d.ts.map +1 -1
- package/dist/graph/algorithms/centrality.js +28 -0
- package/dist/graph/algorithms/centrality.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/algorithms/louvain.d.ts +3 -4
- package/dist/graph/algorithms/louvain.d.ts.map +1 -1
- package/dist/graph/algorithms/louvain.js +29 -0
- package/dist/graph/algorithms/louvain.js.map +1 -1
- package/dist/graph/algorithms/shortest-path.d.ts +2 -0
- package/dist/graph/algorithms/shortest-path.d.ts.map +1 -1
- package/dist/graph/algorithms/shortest-path.js +18 -1
- package/dist/graph/algorithms/shortest-path.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +129 -3
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-clojure.wasm +0 -0
- package/grammars/tree-sitter-cuda.wasm +0 -0
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-fsharp.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/grammars/tree-sitter-groovy.wasm +0 -0
- package/grammars/tree-sitter-julia.wasm +0 -0
- package/grammars/tree-sitter-objc.wasm +0 -0
- package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
- package/grammars/tree-sitter-r.wasm +0 -0
- package/grammars/tree-sitter-solidity.wasm +0 -0
- package/grammars/tree-sitter-verilog.wasm +0 -0
- package/package.json +18 -7
- package/src/ast-analysis/engine.ts +245 -42
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/rules/javascript.ts +0 -1
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +49 -119
- package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/ast.ts +2 -2
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +20 -0
- package/src/db/repository/native-repository.ts +79 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +29 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +67 -76
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +366 -41
- package/src/domain/graph/builder/stages/build-edges.ts +162 -1
- package/src/domain/graph/builder/stages/collect-files.ts +18 -7
- package/src/domain/graph/builder/stages/detect-changes.ts +110 -56
- package/src/domain/graph/builder/stages/finalize.ts +41 -11
- package/src/domain/graph/builder/stages/insert-nodes.ts +75 -39
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +122 -28
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +17 -4
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/clojure.ts +273 -0
- package/src/extractors/cuda.ts +316 -0
- package/src/extractors/erlang.ts +252 -0
- package/src/extractors/fsharp.ts +253 -0
- package/src/extractors/gleam.ts +246 -0
- package/src/extractors/go.ts +43 -33
- package/src/extractors/groovy.ts +332 -0
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/index.ts +11 -0
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +45 -46
- package/src/extractors/julia.ts +318 -0
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/objc.ts +431 -0
- package/src/extractors/ocaml.ts +78 -0
- package/src/extractors/python.ts +31 -25
- package/src/extractors/r.ts +253 -0
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +394 -0
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/verilog.ts +315 -0
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +131 -112
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/bfs.ts +34 -0
- package/src/graph/algorithms/centrality.ts +30 -0
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/algorithms/louvain.ts +31 -4
- package/src/graph/algorithms/shortest-path.ts +20 -1
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +138 -3
|
@@ -6,12 +6,21 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { performance } from 'node:perf_hooks';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
closeDbPair,
|
|
11
|
+
getBuildMeta,
|
|
12
|
+
initSchema,
|
|
13
|
+
MIGRATIONS,
|
|
14
|
+
openDb,
|
|
15
|
+
setBuildMeta,
|
|
16
|
+
} from '../../../db/index.js';
|
|
10
17
|
import { detectWorkspaces, loadConfig } from '../../../infrastructure/config.js';
|
|
11
|
-
import { info, warn } from '../../../infrastructure/logger.js';
|
|
18
|
+
import { debug, info, warn } from '../../../infrastructure/logger.js';
|
|
12
19
|
import { loadNative } from '../../../infrastructure/native.js';
|
|
20
|
+
import { semverCompare } from '../../../infrastructure/update-check.js';
|
|
21
|
+
import { toErrorMessage } from '../../../shared/errors.js';
|
|
13
22
|
import { CODEGRAPH_VERSION } from '../../../shared/version.js';
|
|
14
|
-
import type { BuildGraphOpts, BuildResult } from '../../../types.js';
|
|
23
|
+
import type { BuildGraphOpts, BuildResult, Definition, ExtractorOutput } from '../../../types.js';
|
|
15
24
|
import { getActiveEngine } from '../../parser.js';
|
|
16
25
|
import { setWorkspaces } from '../resolve.js';
|
|
17
26
|
import { PipelineContext } from './context.js';
|
|
@@ -35,18 +44,28 @@ function initializeEngine(ctx: PipelineContext): void {
|
|
|
35
44
|
dataflow: ctx.opts.dataflow !== false,
|
|
36
45
|
ast: ctx.opts.ast !== false,
|
|
37
46
|
nativeDb: ctx.nativeDb,
|
|
38
|
-
// WAL checkpoint callbacks for dual-connection WAL guard (#696).
|
|
47
|
+
// WAL checkpoint callbacks for dual-connection WAL guard (#696, #715).
|
|
39
48
|
// Feature modules (ast, cfg, complexity, dataflow) receive `db` as a
|
|
40
49
|
// parameter and cannot tolerate close/reopen (stale reference). Instead,
|
|
41
|
-
// checkpoint the WAL so native writes start with a clean slate.
|
|
42
|
-
//
|
|
43
|
-
//
|
|
50
|
+
// checkpoint the WAL so native writes start with a clean slate.
|
|
51
|
+
// After native writes, resumeJsDb checkpoints through rusqlite so
|
|
52
|
+
// better-sqlite3 never reads WAL frames from a different SQLite library.
|
|
44
53
|
suspendJsDb: ctx.nativeDb
|
|
45
54
|
? () => {
|
|
46
55
|
ctx.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
47
56
|
}
|
|
48
57
|
: undefined,
|
|
49
|
-
resumeJsDb: ctx.nativeDb
|
|
58
|
+
resumeJsDb: ctx.nativeDb
|
|
59
|
+
? () => {
|
|
60
|
+
try {
|
|
61
|
+
ctx.nativeDb?.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
62
|
+
} catch (e) {
|
|
63
|
+
debug(
|
|
64
|
+
`resumeJsDb: WAL checkpoint failed (nativeDb may already be closed): ${toErrorMessage(e)}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
: undefined,
|
|
50
69
|
};
|
|
51
70
|
const { name: engineName, version: engineVersion } = getActiveEngine(ctx.engineOpts);
|
|
52
71
|
ctx.engineName = engineName as 'native' | 'wasm';
|
|
@@ -120,8 +139,16 @@ function setupPipeline(ctx: PipelineContext): void {
|
|
|
120
139
|
try {
|
|
121
140
|
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
122
141
|
ctx.nativeDb.initSchema();
|
|
142
|
+
// Checkpoint WAL through rusqlite so better-sqlite3 sees a clean DB
|
|
143
|
+
// with no cross-library WAL frames (#715, #717).
|
|
144
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
123
145
|
} catch (err) {
|
|
124
|
-
warn(`NativeDatabase
|
|
146
|
+
warn(`NativeDatabase setup failed, falling back to JS: ${toErrorMessage(err)}`);
|
|
147
|
+
try {
|
|
148
|
+
ctx.nativeDb?.close();
|
|
149
|
+
} catch (e) {
|
|
150
|
+
debug(`setupNativeDb: close failed during fallback: ${toErrorMessage(e)}`);
|
|
151
|
+
}
|
|
125
152
|
ctx.nativeDb = undefined;
|
|
126
153
|
}
|
|
127
154
|
// Always run JS initSchema so better-sqlite3 sees the schema —
|
|
@@ -169,6 +196,58 @@ function formatTimingResult(ctx: PipelineContext): BuildResult {
|
|
|
169
196
|
};
|
|
170
197
|
}
|
|
171
198
|
|
|
199
|
+
// ── NativeDb lifecycle helpers ──────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
/** Checkpoint WAL through rusqlite and close the native connection. */
|
|
202
|
+
function closeNativeDb(ctx: PipelineContext, label: string): void {
|
|
203
|
+
if (!ctx.nativeDb) return;
|
|
204
|
+
try {
|
|
205
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
206
|
+
} catch (e) {
|
|
207
|
+
debug(`${label} WAL checkpoint failed: ${toErrorMessage(e)}`);
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
ctx.nativeDb.close();
|
|
211
|
+
} catch (e) {
|
|
212
|
+
debug(`${label} nativeDb close failed: ${toErrorMessage(e)}`);
|
|
213
|
+
}
|
|
214
|
+
ctx.nativeDb = undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Try to reopen the native connection for a given pipeline phase. */
|
|
218
|
+
function reopenNativeDb(ctx: PipelineContext, label: string): void {
|
|
219
|
+
const native = loadNative();
|
|
220
|
+
if (!native?.NativeDatabase) return;
|
|
221
|
+
try {
|
|
222
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
223
|
+
} catch (e) {
|
|
224
|
+
debug(`reopen nativeDb for ${label} failed: ${toErrorMessage(e)}`);
|
|
225
|
+
ctx.nativeDb = undefined;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Close nativeDb and clear stale references in engineOpts. */
|
|
230
|
+
function suspendNativeDb(ctx: PipelineContext, label: string): void {
|
|
231
|
+
closeNativeDb(ctx, label);
|
|
232
|
+
if (ctx.engineOpts?.nativeDb) {
|
|
233
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* After native writes, reopen the JS db connection to get a fresh page cache.
|
|
239
|
+
* Rusqlite WAL truncation invalidates better-sqlite3's internal WAL index,
|
|
240
|
+
* causing SQLITE_CORRUPT on the next read (#715, #736).
|
|
241
|
+
*/
|
|
242
|
+
function refreshJsDb(ctx: PipelineContext): void {
|
|
243
|
+
try {
|
|
244
|
+
ctx.db.close();
|
|
245
|
+
} catch (e) {
|
|
246
|
+
debug(`refreshJsDb close failed: ${toErrorMessage(e)}`);
|
|
247
|
+
}
|
|
248
|
+
ctx.db = openDb(ctx.dbPath);
|
|
249
|
+
}
|
|
250
|
+
|
|
172
251
|
// ── Pipeline stages execution ───────────────────────────────────────────
|
|
173
252
|
|
|
174
253
|
async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
@@ -179,17 +258,7 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
179
258
|
// that use suspendJsDb/resumeJsDb WAL checkpoint pattern (#696).
|
|
180
259
|
const hadNativeDb = !!ctx.nativeDb;
|
|
181
260
|
if (ctx.db && ctx.nativeDb) {
|
|
182
|
-
|
|
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
|
-
}
|
|
261
|
+
suspendNativeDb(ctx, 'pre-collect');
|
|
193
262
|
}
|
|
194
263
|
|
|
195
264
|
await collectFiles(ctx);
|
|
@@ -198,7 +267,22 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
198
267
|
if (ctx.earlyExit) return;
|
|
199
268
|
|
|
200
269
|
await parseFiles(ctx);
|
|
270
|
+
|
|
271
|
+
// Temporarily reopen nativeDb for insertNodes — it uses the WAL checkpoint
|
|
272
|
+
// guard internally (same pattern as feature modules). Closed again before
|
|
273
|
+
// resolveImports/buildEdges which don't yet have the guard (#709).
|
|
274
|
+
if (hadNativeDb && ctx.engineName === 'native') {
|
|
275
|
+
reopenNativeDb(ctx, 'insertNodes');
|
|
276
|
+
}
|
|
277
|
+
|
|
201
278
|
await insertNodes(ctx);
|
|
279
|
+
|
|
280
|
+
// Close nativeDb after insertNodes — remaining pipeline stages use JS paths.
|
|
281
|
+
if (ctx.nativeDb && ctx.db) {
|
|
282
|
+
closeNativeDb(ctx, 'post-insertNodes');
|
|
283
|
+
refreshJsDb(ctx);
|
|
284
|
+
}
|
|
285
|
+
|
|
202
286
|
await resolveImports(ctx);
|
|
203
287
|
await buildEdges(ctx);
|
|
204
288
|
await buildStructure(ctx);
|
|
@@ -206,33 +290,23 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
206
290
|
// Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow)
|
|
207
291
|
// which use suspendJsDb/resumeJsDb WAL checkpoint before native writes.
|
|
208
292
|
if (hadNativeDb) {
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
} catch {
|
|
217
|
-
ctx.nativeDb = undefined;
|
|
218
|
-
if (ctx.engineOpts) {
|
|
219
|
-
ctx.engineOpts.nativeDb = undefined;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
293
|
+
reopenNativeDb(ctx, 'analyses');
|
|
294
|
+
if (ctx.nativeDb && ctx.engineOpts) {
|
|
295
|
+
ctx.engineOpts.nativeDb = ctx.nativeDb;
|
|
296
|
+
}
|
|
297
|
+
if (!ctx.nativeDb && ctx.engineOpts) {
|
|
298
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
222
299
|
}
|
|
223
300
|
}
|
|
224
301
|
|
|
225
302
|
await runAnalyses(ctx);
|
|
226
303
|
|
|
227
|
-
//
|
|
228
|
-
// and
|
|
304
|
+
// Keep nativeDb open through finalize so persistBuildMetadata, advisory
|
|
305
|
+
// checks, and count queries use the native path. closeDbPair inside
|
|
306
|
+
// finalize handles both connections. Refresh the JS db so it has a
|
|
307
|
+
// valid page cache in case finalize falls back to JS paths (#751).
|
|
229
308
|
if (ctx.nativeDb) {
|
|
230
|
-
|
|
231
|
-
ctx.nativeDb.close();
|
|
232
|
-
} catch {
|
|
233
|
-
/* ignore close errors */
|
|
234
|
-
}
|
|
235
|
-
ctx.nativeDb = undefined;
|
|
309
|
+
refreshJsDb(ctx);
|
|
236
310
|
}
|
|
237
311
|
|
|
238
312
|
await finalize(ctx);
|
|
@@ -256,6 +330,257 @@ export async function buildGraph(
|
|
|
256
330
|
|
|
257
331
|
try {
|
|
258
332
|
setupPipeline(ctx);
|
|
333
|
+
|
|
334
|
+
// ── Rust orchestrator fast path (#695) ────────────────────────────
|
|
335
|
+
// When available, run the entire build pipeline in Rust with zero
|
|
336
|
+
// napi crossings (eliminates WAL dual-connection dance). Falls back
|
|
337
|
+
// to the JS pipeline on failure or when native is unavailable.
|
|
338
|
+
//
|
|
339
|
+
// Native addon 3.8.0 has a path bug: file_symbols keys are absolute
|
|
340
|
+
// paths but known_files are relative, causing zero import/call edges.
|
|
341
|
+
// Skip the orchestrator for affected versions (fixed in 3.9.0+).
|
|
342
|
+
const orchestratorBuggy = !!ctx.engineVersion && semverCompare(ctx.engineVersion, '3.8.0') <= 0;
|
|
343
|
+
const forceJs =
|
|
344
|
+
process.env.CODEGRAPH_FORCE_JS_PIPELINE === '1' ||
|
|
345
|
+
ctx.forceFullRebuild ||
|
|
346
|
+
orchestratorBuggy ||
|
|
347
|
+
ctx.engineName !== 'native';
|
|
348
|
+
if (forceJs) {
|
|
349
|
+
const reason =
|
|
350
|
+
process.env.CODEGRAPH_FORCE_JS_PIPELINE === '1'
|
|
351
|
+
? 'CODEGRAPH_FORCE_JS_PIPELINE=1'
|
|
352
|
+
: ctx.forceFullRebuild
|
|
353
|
+
? 'forceFullRebuild'
|
|
354
|
+
: orchestratorBuggy
|
|
355
|
+
? `buggy addon ${ctx.engineVersion}`
|
|
356
|
+
: `engine=${ctx.engineName}`;
|
|
357
|
+
debug(`Skipping native orchestrator: ${reason}`);
|
|
358
|
+
}
|
|
359
|
+
if (!forceJs && ctx.nativeDb?.buildGraph) {
|
|
360
|
+
try {
|
|
361
|
+
const resultJson = ctx.nativeDb.buildGraph(
|
|
362
|
+
ctx.rootDir,
|
|
363
|
+
JSON.stringify(ctx.config),
|
|
364
|
+
JSON.stringify(ctx.aliases),
|
|
365
|
+
JSON.stringify(opts),
|
|
366
|
+
);
|
|
367
|
+
const result = JSON.parse(resultJson) as {
|
|
368
|
+
phases: Record<string, number>;
|
|
369
|
+
earlyExit?: boolean;
|
|
370
|
+
nodeCount?: number;
|
|
371
|
+
edgeCount?: number;
|
|
372
|
+
fileCount?: number;
|
|
373
|
+
changedFiles?: string[];
|
|
374
|
+
changedCount?: number;
|
|
375
|
+
removedCount?: number;
|
|
376
|
+
isFullBuild?: boolean;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
if (result.earlyExit) {
|
|
380
|
+
info('No changes detected');
|
|
381
|
+
closeDbPair({ db: ctx.db, nativeDb: ctx.nativeDb });
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Log incremental status to match JS pipeline output
|
|
386
|
+
const changed = result.changedCount ?? 0;
|
|
387
|
+
const removed = result.removedCount ?? 0;
|
|
388
|
+
if (!result.isFullBuild && (changed > 0 || removed > 0)) {
|
|
389
|
+
info(`Incremental: ${changed} changed, ${removed} removed`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Map Rust timing fields to the JS BuildResult format.
|
|
393
|
+
// Rust handles collect+detect+parse+insert+resolve+edges+structure+roles.
|
|
394
|
+
const p = result.phases;
|
|
395
|
+
|
|
396
|
+
// Sync build_meta so JS-side version/engine checks work on next build.
|
|
397
|
+
// Note: the Rust orchestrator also writes codegraph_version (using
|
|
398
|
+
// CARGO_PKG_VERSION). We intentionally overwrite it here with the npm
|
|
399
|
+
// package version so that the JS-side "version changed → full rebuild"
|
|
400
|
+
// detection (line ~97) compares against the authoritative JS version.
|
|
401
|
+
// The two versions are kept in lockstep by the release process.
|
|
402
|
+
setBuildMeta(ctx.db, {
|
|
403
|
+
engine: ctx.engineName,
|
|
404
|
+
engine_version: ctx.engineVersion || '',
|
|
405
|
+
codegraph_version: CODEGRAPH_VERSION,
|
|
406
|
+
schema_version: String(ctx.schemaVersion),
|
|
407
|
+
built_at: new Date().toISOString(),
|
|
408
|
+
node_count: String(result.nodeCount ?? 0),
|
|
409
|
+
edge_count: String(result.edgeCount ?? 0),
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
info(
|
|
413
|
+
`Native build orchestrator completed: ${result.nodeCount ?? 0} nodes, ${result.edgeCount ?? 0} edges, ${result.fileCount ?? 0} files`,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
// ── Run analysis phases (AST, complexity, CFG, dataflow) ──────
|
|
417
|
+
// Not yet ported to Rust. After the native orchestrator finishes,
|
|
418
|
+
// reconstruct a minimal fileSymbols map from the DB and run analyses
|
|
419
|
+
// via the JS engine (native standalone functions + WASM fallback).
|
|
420
|
+
let analysisTiming = { astMs: 0, complexityMs: 0, cfgMs: 0, dataflowMs: 0 };
|
|
421
|
+
const needsAnalysis =
|
|
422
|
+
opts.ast !== false ||
|
|
423
|
+
opts.complexity !== false ||
|
|
424
|
+
opts.cfg !== false ||
|
|
425
|
+
opts.dataflow !== false;
|
|
426
|
+
|
|
427
|
+
if (needsAnalysis) {
|
|
428
|
+
// WAL handoff: checkpoint through rusqlite, close nativeDb,
|
|
429
|
+
// reopen better-sqlite3 with a fresh page cache (#715, #736).
|
|
430
|
+
try {
|
|
431
|
+
ctx.nativeDb!.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
432
|
+
} catch {
|
|
433
|
+
/* ignore checkpoint errors */
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
ctx.nativeDb!.close();
|
|
437
|
+
} catch {
|
|
438
|
+
/* ignore close errors */
|
|
439
|
+
}
|
|
440
|
+
ctx.nativeDb = undefined;
|
|
441
|
+
try {
|
|
442
|
+
ctx.db.close();
|
|
443
|
+
} catch {
|
|
444
|
+
/* ignore close errors */
|
|
445
|
+
}
|
|
446
|
+
ctx.db = null!; // avoid closeDbPair operating on a stale handle
|
|
447
|
+
try {
|
|
448
|
+
ctx.db = openDb(ctx.dbPath);
|
|
449
|
+
} catch (reopenErr) {
|
|
450
|
+
warn(
|
|
451
|
+
`Failed to reopen DB for analysis after native build: ${(reopenErr as Error).message}`,
|
|
452
|
+
);
|
|
453
|
+
// Native build succeeded but we can't run analyses — return partial result
|
|
454
|
+
return {
|
|
455
|
+
phases: {
|
|
456
|
+
setupMs: +((p.setupMs ?? 0) + (p.collectMs ?? 0) + (p.detectMs ?? 0)).toFixed(1),
|
|
457
|
+
parseMs: +(p.parseMs ?? 0).toFixed(1),
|
|
458
|
+
insertMs: +(p.insertMs ?? 0).toFixed(1),
|
|
459
|
+
resolveMs: +(p.resolveMs ?? 0).toFixed(1),
|
|
460
|
+
edgesMs: +(p.edgesMs ?? 0).toFixed(1),
|
|
461
|
+
structureMs: +(p.structureMs ?? 0).toFixed(1),
|
|
462
|
+
rolesMs: +(p.rolesMs ?? 0).toFixed(1),
|
|
463
|
+
astMs: 0,
|
|
464
|
+
complexityMs: 0,
|
|
465
|
+
cfgMs: 0,
|
|
466
|
+
dataflowMs: 0,
|
|
467
|
+
finalizeMs: +(p.finalizeMs ?? 0).toFixed(1),
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Reconstruct minimal fileSymbols from DB for analysis visitors.
|
|
473
|
+
// Each entry needs definitions with name/kind/line/endLine so the
|
|
474
|
+
// engine can match complexity/CFG results to the right functions.
|
|
475
|
+
// For incremental builds, scope to only the files that were parsed
|
|
476
|
+
// in this cycle (matching the JS pipeline's behaviour in run-analyses.ts).
|
|
477
|
+
const changedFiles = result.changedFiles;
|
|
478
|
+
let query =
|
|
479
|
+
'SELECT file, name, kind, line, end_line as endLine FROM nodes WHERE file IS NOT NULL';
|
|
480
|
+
const params: string[] = [];
|
|
481
|
+
if (changedFiles && changedFiles.length > 0) {
|
|
482
|
+
const placeholders = changedFiles.map(() => '?').join(',');
|
|
483
|
+
query += ` AND file IN (${placeholders})`;
|
|
484
|
+
params.push(...changedFiles);
|
|
485
|
+
}
|
|
486
|
+
query += ' ORDER BY file, line';
|
|
487
|
+
const rows = ctx.db.prepare(query).all(...params) as {
|
|
488
|
+
file: string;
|
|
489
|
+
name: string;
|
|
490
|
+
kind: string;
|
|
491
|
+
line: number;
|
|
492
|
+
endLine: number | null;
|
|
493
|
+
}[];
|
|
494
|
+
|
|
495
|
+
const fileSymbols = new Map<string, ExtractorOutput>();
|
|
496
|
+
for (const row of rows) {
|
|
497
|
+
let entry = fileSymbols.get(row.file);
|
|
498
|
+
if (!entry) {
|
|
499
|
+
entry = {
|
|
500
|
+
definitions: [],
|
|
501
|
+
calls: [],
|
|
502
|
+
imports: [],
|
|
503
|
+
classes: [],
|
|
504
|
+
exports: [],
|
|
505
|
+
typeMap: new Map(),
|
|
506
|
+
};
|
|
507
|
+
fileSymbols.set(row.file, entry);
|
|
508
|
+
}
|
|
509
|
+
entry.definitions.push({
|
|
510
|
+
name: row.name,
|
|
511
|
+
kind: row.kind as Definition['kind'],
|
|
512
|
+
line: row.line,
|
|
513
|
+
endLine: row.endLine ?? undefined,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Reopen nativeDb for analysis features (suspend/resume WAL pattern).
|
|
518
|
+
const native = loadNative();
|
|
519
|
+
if (native?.NativeDatabase) {
|
|
520
|
+
try {
|
|
521
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
522
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = ctx.nativeDb;
|
|
523
|
+
} catch {
|
|
524
|
+
ctx.nativeDb = undefined;
|
|
525
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = undefined;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const { runAnalyses: runAnalysesFn } = await import('../../../ast-analysis/engine.js');
|
|
531
|
+
analysisTiming = await runAnalysesFn(
|
|
532
|
+
ctx.db,
|
|
533
|
+
fileSymbols,
|
|
534
|
+
ctx.rootDir,
|
|
535
|
+
opts,
|
|
536
|
+
ctx.engineOpts,
|
|
537
|
+
);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
warn(`Analysis phases failed after native build: ${toErrorMessage(err)}`);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Close nativeDb after analyses
|
|
543
|
+
if (ctx.nativeDb) {
|
|
544
|
+
try {
|
|
545
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
546
|
+
} catch {
|
|
547
|
+
/* ignore checkpoint errors */
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
ctx.nativeDb.close();
|
|
551
|
+
} catch {
|
|
552
|
+
/* ignore close errors */
|
|
553
|
+
}
|
|
554
|
+
ctx.nativeDb = undefined;
|
|
555
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = undefined;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
closeDbPair({ db: ctx.db, nativeDb: ctx.nativeDb });
|
|
560
|
+
return {
|
|
561
|
+
phases: {
|
|
562
|
+
setupMs: +((p.setupMs ?? 0) + (p.collectMs ?? 0) + (p.detectMs ?? 0)).toFixed(1),
|
|
563
|
+
parseMs: +(p.parseMs ?? 0).toFixed(1),
|
|
564
|
+
insertMs: +(p.insertMs ?? 0).toFixed(1),
|
|
565
|
+
resolveMs: +(p.resolveMs ?? 0).toFixed(1),
|
|
566
|
+
edgesMs: +(p.edgesMs ?? 0).toFixed(1),
|
|
567
|
+
structureMs: +(p.structureMs ?? 0).toFixed(1),
|
|
568
|
+
rolesMs: +(p.rolesMs ?? 0).toFixed(1),
|
|
569
|
+
astMs: +(analysisTiming.astMs ?? 0).toFixed(1),
|
|
570
|
+
complexityMs: +(analysisTiming.complexityMs ?? 0).toFixed(1),
|
|
571
|
+
cfgMs: +(analysisTiming.cfgMs ?? 0).toFixed(1),
|
|
572
|
+
dataflowMs: +(analysisTiming.dataflowMs ?? 0).toFixed(1),
|
|
573
|
+
finalizeMs: +(p.finalizeMs ?? 0).toFixed(1),
|
|
574
|
+
},
|
|
575
|
+
};
|
|
576
|
+
} catch (err) {
|
|
577
|
+
warn(
|
|
578
|
+
`Native build orchestrator failed, falling back to JS pipeline: ${toErrorMessage(err)}`,
|
|
579
|
+
);
|
|
580
|
+
// Fall through to JS pipeline
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
259
584
|
await runPipelineStages(ctx);
|
|
260
585
|
} catch (err) {
|
|
261
586
|
if (!ctx.earlyExit && ctx.db) {
|
|
@@ -155,6 +155,146 @@ function buildBarrelEdges(
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// ── Import edges (native engine) ────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
function buildImportEdgesNative(
|
|
161
|
+
ctx: PipelineContext,
|
|
162
|
+
getNodeIdStmt: NodeIdStmt,
|
|
163
|
+
allEdgeRows: EdgeRowTuple[],
|
|
164
|
+
native: NativeAddon,
|
|
165
|
+
): void {
|
|
166
|
+
const { fileSymbols, barrelOnlyFiles, rootDir } = ctx;
|
|
167
|
+
|
|
168
|
+
// 1. Build per-file input data
|
|
169
|
+
const files: Array<{
|
|
170
|
+
file: string;
|
|
171
|
+
fileNodeId: number;
|
|
172
|
+
isBarrelOnly: boolean;
|
|
173
|
+
imports: Array<{
|
|
174
|
+
source: string;
|
|
175
|
+
names: string[];
|
|
176
|
+
reexport: boolean;
|
|
177
|
+
typeOnly: boolean;
|
|
178
|
+
dynamicImport: boolean;
|
|
179
|
+
wildcardReexport: boolean;
|
|
180
|
+
}>;
|
|
181
|
+
definitionNames: string[];
|
|
182
|
+
}> = [];
|
|
183
|
+
|
|
184
|
+
// Collect all file node IDs we'll need (sources + targets)
|
|
185
|
+
const fileNodeIds: Array<{ file: string; nodeId: number }> = [];
|
|
186
|
+
const seenNodeFiles = new Set<string>();
|
|
187
|
+
|
|
188
|
+
const addFileNodeId = (relPath: string): { id: number } | undefined => {
|
|
189
|
+
if (seenNodeFiles.has(relPath)) return fileNodeRowCache.get(relPath);
|
|
190
|
+
const row = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
191
|
+
if (row) {
|
|
192
|
+
seenNodeFiles.add(relPath);
|
|
193
|
+
fileNodeIds.push({ file: relPath, nodeId: row.id });
|
|
194
|
+
fileNodeRowCache.set(relPath, row);
|
|
195
|
+
}
|
|
196
|
+
return row;
|
|
197
|
+
};
|
|
198
|
+
const fileNodeRowCache = new Map<string, { id: number }>();
|
|
199
|
+
|
|
200
|
+
// 2. Pre-resolve all imports and build resolved imports array.
|
|
201
|
+
// Keys use forward-slash-normalized rootDir + "/" + relPath to match the Rust
|
|
202
|
+
// lookup format (format!("{}/{}", root_dir.replace('\\', "/"), file)).
|
|
203
|
+
// On Windows, rootDir has backslashes but Rust normalizes them — the JS side
|
|
204
|
+
// must do the same or every resolve key lookup misses (#750).
|
|
205
|
+
const resolvedImports: Array<{ key: string; resolvedPath: string }> = [];
|
|
206
|
+
const fwdRootDir = rootDir.replace(/\\/g, '/');
|
|
207
|
+
|
|
208
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
209
|
+
const fileNodeRow = addFileNodeId(relPath);
|
|
210
|
+
if (!fileNodeRow) continue;
|
|
211
|
+
|
|
212
|
+
const importInfos: Array<{
|
|
213
|
+
source: string;
|
|
214
|
+
names: string[];
|
|
215
|
+
reexport: boolean;
|
|
216
|
+
typeOnly: boolean;
|
|
217
|
+
dynamicImport: boolean;
|
|
218
|
+
wildcardReexport: boolean;
|
|
219
|
+
}> = [];
|
|
220
|
+
|
|
221
|
+
for (const imp of symbols.imports) {
|
|
222
|
+
// Pre-resolve and register target file node
|
|
223
|
+
const resolvedPath = getResolved(ctx, path.join(rootDir, relPath), imp.source);
|
|
224
|
+
addFileNodeId(resolvedPath);
|
|
225
|
+
|
|
226
|
+
// Key matches Rust's format!("{}/{}", root_dir.replace('\\', "/"), file_input.file)
|
|
227
|
+
resolvedImports.push({ key: `${fwdRootDir}/${relPath}|${imp.source}`, resolvedPath });
|
|
228
|
+
|
|
229
|
+
importInfos.push({
|
|
230
|
+
source: imp.source,
|
|
231
|
+
names: imp.names,
|
|
232
|
+
reexport: !!imp.reexport,
|
|
233
|
+
typeOnly: !!imp.typeOnly,
|
|
234
|
+
dynamicImport: !!imp.dynamicImport,
|
|
235
|
+
wildcardReexport: !!imp.wildcardReexport,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
files.push({
|
|
240
|
+
file: relPath,
|
|
241
|
+
fileNodeId: fileNodeRow.id,
|
|
242
|
+
isBarrelOnly: barrelOnlyFiles.has(relPath),
|
|
243
|
+
imports: importInfos,
|
|
244
|
+
definitionNames: symbols.definitions.map((d) => d.name),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 4. Flatten reexportMap
|
|
249
|
+
const fileReexports: Array<{
|
|
250
|
+
file: string;
|
|
251
|
+
reexports: Array<{
|
|
252
|
+
source: string;
|
|
253
|
+
names: string[];
|
|
254
|
+
wildcardReexport: boolean;
|
|
255
|
+
}>;
|
|
256
|
+
}> = [];
|
|
257
|
+
if (ctx.reexportMap) {
|
|
258
|
+
for (const [file, entries] of ctx.reexportMap) {
|
|
259
|
+
const reexports = (
|
|
260
|
+
entries as Array<{ source: string; names: string[]; wildcardReexport: boolean }>
|
|
261
|
+
).map((re) => ({
|
|
262
|
+
source: re.source,
|
|
263
|
+
names: re.names,
|
|
264
|
+
wildcardReexport: !!re.wildcardReexport,
|
|
265
|
+
}));
|
|
266
|
+
fileReexports.push({ file, reexports });
|
|
267
|
+
|
|
268
|
+
// Register reexport target files for node ID lookup
|
|
269
|
+
for (const re of reexports) {
|
|
270
|
+
addFileNodeId(re.source);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 5. Compute barrel file list
|
|
276
|
+
const barrelFiles: string[] = [];
|
|
277
|
+
for (const [relPath] of fileSymbols) {
|
|
278
|
+
if (isBarrelFile(ctx, relPath)) {
|
|
279
|
+
barrelFiles.push(relPath);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 6. Call native
|
|
284
|
+
const nativeEdges = native.buildImportEdges!(
|
|
285
|
+
files,
|
|
286
|
+
resolvedImports,
|
|
287
|
+
fileReexports,
|
|
288
|
+
fileNodeIds,
|
|
289
|
+
barrelFiles,
|
|
290
|
+
rootDir,
|
|
291
|
+
) as NativeEdge[];
|
|
292
|
+
|
|
293
|
+
for (const e of nativeEdges) {
|
|
294
|
+
allEdgeRows.push([e.sourceId, e.targetId, e.kind, e.confidence, e.dynamic]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
158
298
|
// ── Call edges (native engine) ──────────────────────────────────────────
|
|
159
299
|
|
|
160
300
|
function buildCallEdgesNative(
|
|
@@ -594,7 +734,28 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
|
|
|
594
734
|
}
|
|
595
735
|
}
|
|
596
736
|
|
|
597
|
-
|
|
737
|
+
// Skip native import-edge path for small incremental builds (≤3 files):
|
|
738
|
+
// napi-rs marshaling overhead exceeds computation savings.
|
|
739
|
+
const useNativeImportEdges =
|
|
740
|
+
native?.buildImportEdges && (ctx.isFullBuild || ctx.fileSymbols.size > 3);
|
|
741
|
+
if (useNativeImportEdges) {
|
|
742
|
+
const beforeLen = allEdgeRows.length;
|
|
743
|
+
buildImportEdgesNative(ctx, getNodeIdStmt, allEdgeRows, native!);
|
|
744
|
+
// Fallback: if native produced 0 import edges but there are imports to
|
|
745
|
+
// process, the native binary may have a key-format mismatch (e.g. Windows
|
|
746
|
+
// path separators — #750). Retry with the JS implementation.
|
|
747
|
+
// NOTE: This also fires for codebases where every import targets an
|
|
748
|
+
// external package (npm deps) that the resolver intentionally skips.
|
|
749
|
+
// In that case the JS path resolves zero edges too, so the only cost
|
|
750
|
+
// is the redundant JS traversal — no correctness impact.
|
|
751
|
+
const hasImports = [...ctx.fileSymbols.values()].some((s) => s.imports.length > 0);
|
|
752
|
+
if (allEdgeRows.length === beforeLen && hasImports) {
|
|
753
|
+
debug('Native buildImportEdges produced 0 edges — falling back to JS');
|
|
754
|
+
buildImportEdges(ctx, getNodeIdStmt, allEdgeRows);
|
|
755
|
+
}
|
|
756
|
+
} else {
|
|
757
|
+
buildImportEdges(ctx, getNodeIdStmt, allEdgeRows);
|
|
758
|
+
}
|
|
598
759
|
|
|
599
760
|
// Skip native call-edge path for small incremental builds (≤3 files):
|
|
600
761
|
// napi-rs marshaling overhead for allNodes exceeds computation savings.
|