@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
package/src/features/ast.ts
CHANGED
|
@@ -12,10 +12,9 @@ import type { ASTNodeKind, BetterSqlite3Database, Definition, TreeSitterNode } f
|
|
|
12
12
|
|
|
13
13
|
// ─── Constants ────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
|
-
export const AST_NODE_KINDS: ASTNodeKind[] = ['
|
|
15
|
+
export const AST_NODE_KINDS: ASTNodeKind[] = ['new', 'string', 'regex', 'throw', 'await'];
|
|
16
16
|
|
|
17
17
|
const KIND_ICONS: Record<string, string> = {
|
|
18
|
-
call: '\u0192', // ƒ
|
|
19
18
|
new: '\u2295', // ⊕
|
|
20
19
|
string: '"',
|
|
21
20
|
regex: '/',
|
|
@@ -59,38 +58,11 @@ function findParentDef(defs: Definition[], line: number): Definition | null {
|
|
|
59
58
|
return best;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
// ─── Build
|
|
61
|
+
// ─── Build helpers ───────────────────────────────────────────────────
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
_rootDir: string,
|
|
68
|
-
engineOpts?: {
|
|
69
|
-
nativeDb?: {
|
|
70
|
-
bulkInsertAstNodes(
|
|
71
|
-
batches: Array<{
|
|
72
|
-
file: string;
|
|
73
|
-
nodes: Array<{
|
|
74
|
-
line: number;
|
|
75
|
-
kind: string;
|
|
76
|
-
name: string;
|
|
77
|
-
text?: string | null;
|
|
78
|
-
receiver?: string | null;
|
|
79
|
-
}>;
|
|
80
|
-
}>,
|
|
81
|
-
): number;
|
|
82
|
-
};
|
|
83
|
-
suspendJsDb?: () => void;
|
|
84
|
-
resumeJsDb?: () => void;
|
|
85
|
-
},
|
|
86
|
-
): Promise<void> {
|
|
87
|
-
// ── Native bulk-insert fast path ──────────────────────────────────────
|
|
88
|
-
// Uses NativeDatabase persistent connection (Phase 6.15+).
|
|
89
|
-
// Standalone napi functions were removed in 6.17.
|
|
90
|
-
const nativeDb = engineOpts?.nativeDb;
|
|
91
|
-
if (nativeDb?.bulkInsertAstNodes) {
|
|
92
|
-
let needsJsFallback = false;
|
|
93
|
-
const batches: Array<{
|
|
63
|
+
interface NativeDbHandle {
|
|
64
|
+
bulkInsertAstNodes(
|
|
65
|
+
batches: Array<{
|
|
94
66
|
file: string;
|
|
95
67
|
nodes: Array<{
|
|
96
68
|
line: number;
|
|
@@ -99,47 +71,127 @@ export async function buildAstNodes(
|
|
|
99
71
|
text?: string | null;
|
|
100
72
|
receiver?: string | null;
|
|
101
73
|
}>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (Array.isArray(symbols.astNodes)) {
|
|
106
|
-
batches.push({
|
|
107
|
-
file: relPath,
|
|
108
|
-
nodes: symbols.astNodes.map((n) => ({
|
|
109
|
-
line: n.line,
|
|
110
|
-
kind: n.kind,
|
|
111
|
-
name: n.name,
|
|
112
|
-
text: n.text,
|
|
113
|
-
receiver: n.receiver ?? '',
|
|
114
|
-
})),
|
|
115
|
-
});
|
|
116
|
-
} else if (symbols.calls || symbols._tree) {
|
|
117
|
-
needsJsFallback = true;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
74
|
+
}>,
|
|
75
|
+
): number;
|
|
76
|
+
}
|
|
121
77
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
78
|
+
interface EngineOpts {
|
|
79
|
+
nativeDb?: NativeDbHandle;
|
|
80
|
+
suspendJsDb?: () => void;
|
|
81
|
+
resumeJsDb?: () => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Attempt native bulk-insert of AST nodes.
|
|
86
|
+
* Returns `true` if all nodes were inserted natively, `false` if JS fallback is needed.
|
|
87
|
+
*/
|
|
88
|
+
function tryNativeBulkInsert(
|
|
89
|
+
fileSymbols: Map<string, FileSymbols>,
|
|
90
|
+
engineOpts: EngineOpts | undefined,
|
|
91
|
+
): boolean {
|
|
92
|
+
const nativeDb = engineOpts?.nativeDb;
|
|
93
|
+
if (!nativeDb?.bulkInsertAstNodes) return false;
|
|
94
|
+
|
|
95
|
+
const batches: Array<{
|
|
96
|
+
file: string;
|
|
97
|
+
nodes: Array<{
|
|
98
|
+
line: number;
|
|
99
|
+
kind: string;
|
|
100
|
+
name: string;
|
|
101
|
+
text?: string | null;
|
|
102
|
+
receiver?: string | null;
|
|
103
|
+
}>;
|
|
104
|
+
}> = [];
|
|
105
|
+
|
|
106
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
107
|
+
if (Array.isArray(symbols.astNodes)) {
|
|
108
|
+
batches.push({
|
|
109
|
+
file: relPath,
|
|
110
|
+
nodes: symbols.astNodes.map((n) => ({
|
|
111
|
+
line: n.line,
|
|
112
|
+
kind: n.kind,
|
|
113
|
+
name: n.name,
|
|
114
|
+
text: n.text,
|
|
115
|
+
receiver: n.receiver ?? '',
|
|
116
|
+
})),
|
|
117
|
+
});
|
|
118
|
+
} else if (symbols.calls || symbols._tree) {
|
|
119
|
+
return false; // needs JS fallback
|
|
139
120
|
}
|
|
140
121
|
}
|
|
141
122
|
|
|
142
|
-
|
|
123
|
+
const expectedNodes = batches.reduce((s, b) => s + b.nodes.length, 0);
|
|
124
|
+
let inserted: number;
|
|
125
|
+
try {
|
|
126
|
+
engineOpts?.suspendJsDb?.();
|
|
127
|
+
inserted = nativeDb.bulkInsertAstNodes(batches);
|
|
128
|
+
} finally {
|
|
129
|
+
engineOpts?.resumeJsDb?.();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (inserted === expectedNodes) {
|
|
133
|
+
debug(`AST extraction (native bulk): ${inserted} nodes stored`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
debug(
|
|
137
|
+
`AST extraction (native bulk): expected ${expectedNodes}, got ${inserted} — falling back to JS`,
|
|
138
|
+
);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Collect AST rows for a single file, resolving parent node IDs. */
|
|
143
|
+
function collectFileAstRows(
|
|
144
|
+
db: BetterSqlite3Database,
|
|
145
|
+
relPath: string,
|
|
146
|
+
symbols: FileSymbols,
|
|
147
|
+
): AstRow[] {
|
|
148
|
+
const defs = symbols.definitions || [];
|
|
149
|
+
const nodeIdMap = new Map<string, number>();
|
|
150
|
+
for (const row of bulkNodeIdsByFile(db, relPath)) {
|
|
151
|
+
nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (Array.isArray(symbols.astNodes)) {
|
|
155
|
+
return symbols.astNodes.map((n) => {
|
|
156
|
+
const parentDef = findParentDef(defs, n.line);
|
|
157
|
+
const parentNodeId = parentDef
|
|
158
|
+
? nodeIdMap.get(`${parentDef.name}|${parentDef.kind}|${parentDef.line}`) || null
|
|
159
|
+
: null;
|
|
160
|
+
return {
|
|
161
|
+
file: relPath,
|
|
162
|
+
line: n.line,
|
|
163
|
+
kind: n.kind,
|
|
164
|
+
name: n.name,
|
|
165
|
+
text: n.text || null,
|
|
166
|
+
receiver: n.receiver || null,
|
|
167
|
+
parentNodeId,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// WASM fallback — walk tree if available
|
|
173
|
+
const ext = path.extname(relPath).toLowerCase();
|
|
174
|
+
if (WALK_EXTENSIONS.has(ext) && symbols._tree) {
|
|
175
|
+
const rows: AstRow[] = [];
|
|
176
|
+
walkAst(symbols._tree.rootNode, defs, relPath, rows, nodeIdMap);
|
|
177
|
+
return rows;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── Build ────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export async function buildAstNodes(
|
|
186
|
+
db: BetterSqlite3Database,
|
|
187
|
+
fileSymbols: Map<string, FileSymbols>,
|
|
188
|
+
_rootDir: string,
|
|
189
|
+
engineOpts?: EngineOpts,
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
// Native bulk-insert fast path (Phase 6.15+)
|
|
192
|
+
if (tryNativeBulkInsert(fileSymbols, engineOpts)) return;
|
|
193
|
+
|
|
194
|
+
// JS fallback path
|
|
143
195
|
let insertStmt: ReturnType<BetterSqlite3Database['prepare']>;
|
|
144
196
|
try {
|
|
145
197
|
insertStmt = db.prepare(
|
|
@@ -157,43 +209,8 @@ export async function buildAstNodes(
|
|
|
157
209
|
});
|
|
158
210
|
|
|
159
211
|
const allRows: AstRow[] = [];
|
|
160
|
-
|
|
161
212
|
for (const [relPath, symbols] of fileSymbols) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const nodeIdMap = new Map<string, number>();
|
|
165
|
-
for (const row of bulkNodeIdsByFile(db, relPath)) {
|
|
166
|
-
nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (Array.isArray(symbols.astNodes)) {
|
|
170
|
-
// Native engine provided AST nodes (may be empty for files with no AST content)
|
|
171
|
-
for (const n of symbols.astNodes) {
|
|
172
|
-
const parentDef = findParentDef(defs, n.line);
|
|
173
|
-
let parentNodeId: number | null = null;
|
|
174
|
-
if (parentDef) {
|
|
175
|
-
parentNodeId =
|
|
176
|
-
nodeIdMap.get(`${parentDef.name}|${parentDef.kind}|${parentDef.line}`) || null;
|
|
177
|
-
}
|
|
178
|
-
allRows.push({
|
|
179
|
-
file: relPath,
|
|
180
|
-
line: n.line,
|
|
181
|
-
kind: n.kind,
|
|
182
|
-
name: n.name,
|
|
183
|
-
text: n.text || null,
|
|
184
|
-
receiver: n.receiver || null,
|
|
185
|
-
parentNodeId,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
// WASM fallback — walk tree if available
|
|
190
|
-
const ext = path.extname(relPath).toLowerCase();
|
|
191
|
-
if (WALK_EXTENSIONS.has(ext) && symbols._tree) {
|
|
192
|
-
const astRows: AstRow[] = [];
|
|
193
|
-
walkAst(symbols._tree.rootNode, defs, relPath, astRows, nodeIdMap);
|
|
194
|
-
allRows.push(...astRows);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
213
|
+
allRows.push(...collectFileAstRows(db, relPath, symbols));
|
|
197
214
|
}
|
|
198
215
|
|
|
199
216
|
if (allRows.length > 0) {
|
|
@@ -351,23 +368,25 @@ export function astQuery(
|
|
|
351
368
|
if (outputResult(data, 'results', opts)) return;
|
|
352
369
|
|
|
353
370
|
if (data.results.length === 0) {
|
|
354
|
-
|
|
371
|
+
process.stdout.write(`No AST nodes found${pattern ? ` matching "${pattern}"` : ''}.\n`);
|
|
355
372
|
return;
|
|
356
373
|
}
|
|
357
374
|
|
|
358
375
|
const kindLabel = opts.kind ? ` (kind: ${opts.kind})` : '';
|
|
359
|
-
|
|
376
|
+
process.stdout.write(
|
|
377
|
+
`\n${data.count} AST nodes${pattern ? ` matching "${pattern}"` : ''}${kindLabel}:\n\n`,
|
|
378
|
+
);
|
|
360
379
|
|
|
361
380
|
for (const r of data.results) {
|
|
362
381
|
const icon = KIND_ICONS[r.kind] || '?';
|
|
363
382
|
const parentInfo = r.parent ? ` (in ${r.parent.name})` : '';
|
|
364
|
-
|
|
383
|
+
process.stdout.write(` ${icon} ${r.name} -- ${r.file}:${r.line}${parentInfo}\n`);
|
|
365
384
|
}
|
|
366
385
|
|
|
367
386
|
if (data._pagination?.hasMore) {
|
|
368
|
-
|
|
369
|
-
`\n ... ${data._pagination.total - data._pagination.offset - data._pagination.returned} more (use --offset ${data._pagination.offset + data._pagination.limit})`,
|
|
387
|
+
process.stdout.write(
|
|
388
|
+
`\n ... ${data._pagination.total - data._pagination.offset - data._pagination.returned} more (use --offset ${data._pagination.offset + data._pagination.limit})\n`,
|
|
370
389
|
);
|
|
371
390
|
}
|
|
372
|
-
|
|
391
|
+
process.stdout.write('\n');
|
|
373
392
|
}
|
package/src/features/audit.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { normalizeFileFilter } from '../db/query-builder.js';
|
|
|
4
4
|
import { bfsTransitiveCallers } from '../domain/analysis/impact.js';
|
|
5
5
|
import { explainData } from '../domain/queries.js';
|
|
6
6
|
import { loadConfig } from '../infrastructure/config.js';
|
|
7
|
+
import { debug } from '../infrastructure/logger.js';
|
|
7
8
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
9
|
+
import { toErrorMessage } from '../shared/errors.js';
|
|
8
10
|
import type { BetterSqlite3Database, CodegraphConfig } from '../types.js';
|
|
9
11
|
import { RULE_DEFS } from './manifesto.js';
|
|
10
12
|
|
|
@@ -41,8 +43,8 @@ function resolveThresholds(
|
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
return resolved;
|
|
44
|
-
} catch {
|
|
45
|
-
|
|
46
|
+
} catch (e) {
|
|
47
|
+
debug(`resolveThresholds: config loading failed, using defaults: ${toErrorMessage(e)}`);
|
|
46
48
|
const resolved: Record<string, ThresholdEntry> = {};
|
|
47
49
|
for (const def of FUNCTION_RULES) {
|
|
48
50
|
resolved[def.name] = {
|
|
@@ -110,8 +112,8 @@ function readPhase44(db: BetterSqlite3Database, nodeId: number): Phase44Fields {
|
|
|
110
112
|
sideEffects: row.side_effects ?? null,
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
|
-
} catch {
|
|
114
|
-
|
|
115
|
+
} catch (e) {
|
|
116
|
+
debug(`readPhase44: columns may not exist yet: ${toErrorMessage(e)}`);
|
|
115
117
|
}
|
|
116
118
|
return { riskScore: null, complexityNotes: null, sideEffects: null };
|
|
117
119
|
}
|
|
@@ -413,8 +415,8 @@ function buildHealth(
|
|
|
413
415
|
commentLines: row.comment_lines || 0,
|
|
414
416
|
thresholdBreaches: checkBreaches(row as unknown as Record<string, unknown>, thresholds),
|
|
415
417
|
};
|
|
416
|
-
} catch {
|
|
417
|
-
|
|
418
|
+
} catch (e) {
|
|
419
|
+
debug(`readHealth: function_complexity table may not exist: ${toErrorMessage(e)}`);
|
|
418
420
|
return defaultHealth();
|
|
419
421
|
}
|
|
420
422
|
}
|
|
@@ -8,6 +8,7 @@ import { kindIcon } from '../domain/queries.js';
|
|
|
8
8
|
import { debug } from '../infrastructure/logger.js';
|
|
9
9
|
import { getNative, isNativeAvailable } from '../infrastructure/native.js';
|
|
10
10
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
11
|
+
import { toErrorMessage } from '../shared/errors.js';
|
|
11
12
|
import type { EngineMode, NativeDatabase } from '../types.js';
|
|
12
13
|
|
|
13
14
|
// ─── Git Helpers ────────────────────────────────────────────────────────
|
|
@@ -20,7 +21,8 @@ function validateGitRef(repoRoot: string, ref: string): string | null {
|
|
|
20
21
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
21
22
|
}).trim();
|
|
22
23
|
return sha;
|
|
23
|
-
} catch {
|
|
24
|
+
} catch (e) {
|
|
25
|
+
debug(`validateGitRef failed for "${ref}": ${toErrorMessage(e)}`);
|
|
24
26
|
return null;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -50,11 +52,12 @@ function removeWorktree(repoRoot: string, dir: string): void {
|
|
|
50
52
|
encoding: 'utf-8',
|
|
51
53
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
52
54
|
});
|
|
53
|
-
} catch {
|
|
55
|
+
} catch (e) {
|
|
56
|
+
debug(`removeWorktree: git worktree remove failed for ${dir}: ${toErrorMessage(e)}`);
|
|
54
57
|
try {
|
|
55
58
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
56
|
-
} catch {
|
|
57
|
-
|
|
59
|
+
} catch (rmErr) {
|
|
60
|
+
debug(`removeWorktree: rmSync fallback failed for ${dir}: ${toErrorMessage(rmErr)}`);
|
|
58
61
|
}
|
|
59
62
|
try {
|
|
60
63
|
execFileSync('git', ['worktree', 'prune'], {
|
|
@@ -62,8 +65,8 @@ function removeWorktree(repoRoot: string, dir: string): void {
|
|
|
62
65
|
encoding: 'utf-8',
|
|
63
66
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
64
67
|
});
|
|
65
|
-
} catch {
|
|
66
|
-
|
|
68
|
+
} catch (pruneErr) {
|
|
69
|
+
debug(`removeWorktree: git worktree prune failed: ${toErrorMessage(pruneErr)}`);
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -117,7 +120,7 @@ function loadSymbolsFromDb(
|
|
|
117
120
|
const native = getNative();
|
|
118
121
|
nativeDb = native.NativeDatabase.openReadonly(dbPath);
|
|
119
122
|
} catch (e) {
|
|
120
|
-
debug(`loadSymbolsFromDb: native path failed: ${(e
|
|
123
|
+
debug(`loadSymbolsFromDb: native path failed: ${toErrorMessage(e)}`);
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -205,8 +208,8 @@ function loadSymbolsFromDb(
|
|
|
205
208
|
if (nativeDb) {
|
|
206
209
|
try {
|
|
207
210
|
nativeDb.close();
|
|
208
|
-
} catch {
|
|
209
|
-
|
|
211
|
+
} catch (e) {
|
|
212
|
+
debug(`loadSymbolsFromDb: nativeDb close failed: ${toErrorMessage(e)}`);
|
|
210
213
|
}
|
|
211
214
|
}
|
|
212
215
|
}
|
|
@@ -360,6 +363,38 @@ interface BranchCompareResult {
|
|
|
360
363
|
summary?: BranchCompareSummary;
|
|
361
364
|
}
|
|
362
365
|
|
|
366
|
+
function attachImpactToSymbols(
|
|
367
|
+
symbols: SymbolInfo[],
|
|
368
|
+
dbPath: string,
|
|
369
|
+
_baseSymbols: Map<string, SymbolInfo>,
|
|
370
|
+
maxDepth: number,
|
|
371
|
+
noTests: boolean,
|
|
372
|
+
): void {
|
|
373
|
+
for (const sym of symbols) {
|
|
374
|
+
const symCallers = loadCallersFromDb(dbPath, sym.id ? [sym.id] : [], maxDepth, noTests);
|
|
375
|
+
(sym as SymbolInfo & { impact?: CallerInfo[] }).impact = symCallers;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function attachImpactToChanged(
|
|
380
|
+
changed: ChangedSymbol[],
|
|
381
|
+
dbPath: string,
|
|
382
|
+
baseSymbols: Map<string, SymbolInfo>,
|
|
383
|
+
maxDepth: number,
|
|
384
|
+
noTests: boolean,
|
|
385
|
+
): void {
|
|
386
|
+
for (const sym of changed) {
|
|
387
|
+
const baseSym = baseSymbols.get(makeSymbolKey(sym.kind, sym.file, sym.name));
|
|
388
|
+
const symCallers = loadCallersFromDb(
|
|
389
|
+
dbPath,
|
|
390
|
+
baseSym?.id ? [baseSym.id] : [],
|
|
391
|
+
maxDepth,
|
|
392
|
+
noTests,
|
|
393
|
+
);
|
|
394
|
+
sym.impact = symCallers;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
363
398
|
export async function branchCompareData(
|
|
364
399
|
baseRef: string,
|
|
365
400
|
targetRef: string,
|
|
@@ -376,7 +411,8 @@ export async function branchCompareData(
|
|
|
376
411
|
encoding: 'utf-8',
|
|
377
412
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
378
413
|
});
|
|
379
|
-
} catch {
|
|
414
|
+
} catch (e) {
|
|
415
|
+
debug(`branchCompareData: git check failed: ${toErrorMessage(e)}`);
|
|
380
416
|
return { error: 'Not a git repository' };
|
|
381
417
|
}
|
|
382
418
|
|
|
@@ -440,20 +476,8 @@ export async function branchCompareData(
|
|
|
440
476
|
const removedImpact = loadCallersFromDb(baseDbPath, removedIds, maxDepth, noTests);
|
|
441
477
|
const changedImpact = loadCallersFromDb(baseDbPath, changedIds, maxDepth, noTests);
|
|
442
478
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
(sym as SymbolInfo & { impact?: CallerInfo[] }).impact = symCallers;
|
|
446
|
-
}
|
|
447
|
-
for (const sym of changed) {
|
|
448
|
-
const baseSym = baseSymbols.get(makeSymbolKey(sym.kind, sym.file, sym.name));
|
|
449
|
-
const symCallers = loadCallersFromDb(
|
|
450
|
-
baseDbPath,
|
|
451
|
-
baseSym?.id ? [baseSym.id] : [],
|
|
452
|
-
maxDepth,
|
|
453
|
-
noTests,
|
|
454
|
-
);
|
|
455
|
-
sym.impact = symCallers;
|
|
456
|
-
}
|
|
479
|
+
attachImpactToSymbols(removed, baseDbPath, baseSymbols, maxDepth, noTests);
|
|
480
|
+
attachImpactToChanged(changed, baseDbPath, baseSymbols, maxDepth, noTests);
|
|
457
481
|
|
|
458
482
|
const allImpacted = new Set<string>();
|
|
459
483
|
for (const c of removedImpact) allImpacted.add(`${c.file}:${c.name}`);
|
|
@@ -489,20 +513,66 @@ export async function branchCompareData(
|
|
|
489
513
|
},
|
|
490
514
|
};
|
|
491
515
|
} catch (err) {
|
|
492
|
-
return { error: (err
|
|
516
|
+
return { error: toErrorMessage(err) };
|
|
493
517
|
} finally {
|
|
494
518
|
removeWorktree(repoRoot, baseDir);
|
|
495
519
|
removeWorktree(repoRoot, targetDir);
|
|
496
520
|
try {
|
|
497
521
|
fs.rmSync(tmpBase, { recursive: true, force: true });
|
|
498
|
-
} catch {
|
|
499
|
-
|
|
522
|
+
} catch (cleanupErr) {
|
|
523
|
+
debug(`branchCompareData: temp cleanup failed: ${toErrorMessage(cleanupErr)}`);
|
|
500
524
|
}
|
|
501
525
|
}
|
|
502
526
|
}
|
|
503
527
|
|
|
504
528
|
// ─── Mermaid Output ─────────────────────────────────────────────────────
|
|
505
529
|
|
|
530
|
+
interface MermaidNodeIdState {
|
|
531
|
+
counter: number;
|
|
532
|
+
map: Map<string, string>;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function mermaidNodeId(state: MermaidNodeIdState, key: string): string {
|
|
536
|
+
if (!state.map.has(key)) {
|
|
537
|
+
state.map.set(key, `n${state.counter++}`);
|
|
538
|
+
}
|
|
539
|
+
return state.map.get(key)!;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function addMermaidSubgraph(
|
|
543
|
+
lines: string[],
|
|
544
|
+
state: MermaidNodeIdState,
|
|
545
|
+
prefix: string,
|
|
546
|
+
label: string,
|
|
547
|
+
symbols: Array<{ kind: string; file: string; name: string }>,
|
|
548
|
+
fillColor: string,
|
|
549
|
+
strokeColor: string,
|
|
550
|
+
): void {
|
|
551
|
+
if (symbols.length === 0) return;
|
|
552
|
+
lines.push(` subgraph sg_${prefix}["${label}"]`);
|
|
553
|
+
for (const sym of symbols) {
|
|
554
|
+
const key = `${prefix}::${sym.kind}::${sym.file}::${sym.name}`;
|
|
555
|
+
const nid = mermaidNodeId(state, key);
|
|
556
|
+
lines.push(` ${nid}["[${kindIcon(sym.kind)}] ${sym.name}"]`);
|
|
557
|
+
}
|
|
558
|
+
lines.push(' end');
|
|
559
|
+
lines.push(` style sg_${prefix} fill:${fillColor},stroke:${strokeColor}`);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function collectImpactedCallers(
|
|
563
|
+
impactSources: Array<{ impact?: CallerInfo[] }>,
|
|
564
|
+
): Map<string, CallerInfo> {
|
|
565
|
+
const allImpacted = new Map<string, CallerInfo>();
|
|
566
|
+
for (const sym of impactSources) {
|
|
567
|
+
if (!sym.impact) continue;
|
|
568
|
+
for (const c of sym.impact) {
|
|
569
|
+
const key = `impact::${c.kind}::${c.file}::${c.name}`;
|
|
570
|
+
if (!allImpacted.has(key)) allImpacted.set(key, c);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return allImpacted;
|
|
574
|
+
}
|
|
575
|
+
|
|
506
576
|
export function branchCompareMermaid(data: BranchCompareResult): string {
|
|
507
577
|
if (data.error) return data.error;
|
|
508
578
|
if (
|
|
@@ -514,63 +584,19 @@ export function branchCompareMermaid(data: BranchCompareResult): string {
|
|
|
514
584
|
}
|
|
515
585
|
|
|
516
586
|
const lines = ['flowchart TB'];
|
|
517
|
-
|
|
518
|
-
const nodeIdMap = new Map<string, string>();
|
|
519
|
-
|
|
520
|
-
function nodeId(key: string): string {
|
|
521
|
-
if (!nodeIdMap.has(key)) {
|
|
522
|
-
nodeIdMap.set(key, `n${nodeCounter++}`);
|
|
523
|
-
}
|
|
524
|
-
return nodeIdMap.get(key)!;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (data.added && data.added.length > 0) {
|
|
528
|
-
lines.push(' subgraph sg_added["Added"]');
|
|
529
|
-
for (const sym of data.added) {
|
|
530
|
-
const key = `added::${sym.kind}::${sym.file}::${sym.name}`;
|
|
531
|
-
const nid = nodeId(key);
|
|
532
|
-
lines.push(` ${nid}["[${kindIcon(sym.kind)}] ${sym.name}"]`);
|
|
533
|
-
}
|
|
534
|
-
lines.push(' end');
|
|
535
|
-
lines.push(' style sg_added fill:#e8f5e9,stroke:#4caf50');
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (data.removed && data.removed.length > 0) {
|
|
539
|
-
lines.push(' subgraph sg_removed["Removed"]');
|
|
540
|
-
for (const sym of data.removed) {
|
|
541
|
-
const key = `removed::${sym.kind}::${sym.file}::${sym.name}`;
|
|
542
|
-
const nid = nodeId(key);
|
|
543
|
-
lines.push(` ${nid}["[${kindIcon(sym.kind)}] ${sym.name}"]`);
|
|
544
|
-
}
|
|
545
|
-
lines.push(' end');
|
|
546
|
-
lines.push(' style sg_removed fill:#ffebee,stroke:#f44336');
|
|
547
|
-
}
|
|
587
|
+
const state: MermaidNodeIdState = { counter: 0, map: new Map() };
|
|
548
588
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const key = `changed::${sym.kind}::${sym.file}::${sym.name}`;
|
|
553
|
-
const nid = nodeId(key);
|
|
554
|
-
lines.push(` ${nid}["[${kindIcon(sym.kind)}] ${sym.name}"]`);
|
|
555
|
-
}
|
|
556
|
-
lines.push(' end');
|
|
557
|
-
lines.push(' style sg_changed fill:#fff3e0,stroke:#ff9800');
|
|
558
|
-
}
|
|
589
|
+
addMermaidSubgraph(lines, state, 'added', 'Added', data.added || [], '#e8f5e9', '#4caf50');
|
|
590
|
+
addMermaidSubgraph(lines, state, 'removed', 'Removed', data.removed || [], '#ffebee', '#f44336');
|
|
591
|
+
addMermaidSubgraph(lines, state, 'changed', 'Changed', data.changed || [], '#fff3e0', '#ff9800');
|
|
559
592
|
|
|
560
|
-
const allImpacted = new Map<string, CallerInfo>();
|
|
561
593
|
const impactSources = [...(data.removed || []), ...(data.changed || [])];
|
|
562
|
-
|
|
563
|
-
if (!sym.impact) continue;
|
|
564
|
-
for (const c of sym.impact) {
|
|
565
|
-
const key = `impact::${c.kind}::${c.file}::${c.name}`;
|
|
566
|
-
if (!allImpacted.has(key)) allImpacted.set(key, c);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
594
|
+
const allImpacted = collectImpactedCallers(impactSources);
|
|
569
595
|
|
|
570
596
|
if (allImpacted.size > 0) {
|
|
571
597
|
lines.push(' subgraph sg_impact["Impacted Callers"]');
|
|
572
598
|
for (const [key, c] of allImpacted) {
|
|
573
|
-
const nid =
|
|
599
|
+
const nid = mermaidNodeId(state, key);
|
|
574
600
|
lines.push(` ${nid}["[${kindIcon(c.kind)}] ${c.name}"]`);
|
|
575
601
|
}
|
|
576
602
|
lines.push(' end');
|
|
@@ -583,8 +609,8 @@ export function branchCompareMermaid(data: BranchCompareResult): string {
|
|
|
583
609
|
const symKey = `${prefix}::${sym.kind}::${sym.file}::${sym.name}`;
|
|
584
610
|
for (const c of sym.impact) {
|
|
585
611
|
const callerKey = `impact::${c.kind}::${c.file}::${c.name}`;
|
|
586
|
-
if (
|
|
587
|
-
lines.push(` ${
|
|
612
|
+
if (state.map.has(symKey) && state.map.has(callerKey)) {
|
|
613
|
+
lines.push(` ${state.map.get(symKey)} -.-> ${state.map.get(callerKey)}`);
|
|
588
614
|
}
|
|
589
615
|
}
|
|
590
616
|
}
|
|
@@ -86,10 +86,7 @@ interface DriftResult {
|
|
|
86
86
|
driftScore: number;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function
|
|
90
|
-
communities: CommunityObject[],
|
|
91
|
-
communityDirs: Map<number, Set<string>>,
|
|
92
|
-
): DriftResult {
|
|
89
|
+
function buildDirToCommunities(communityDirs: Map<number, Set<string>>): Map<string, Set<number>> {
|
|
93
90
|
const dirToCommunities = new Map<string, Set<number>>();
|
|
94
91
|
for (const [cid, dirs] of communityDirs) {
|
|
95
92
|
for (const dir of dirs) {
|
|
@@ -97,20 +94,28 @@ function analyzeDrift(
|
|
|
97
94
|
dirToCommunities.get(dir)!.add(cid);
|
|
98
95
|
}
|
|
99
96
|
}
|
|
97
|
+
return dirToCommunities;
|
|
98
|
+
}
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
function findSplitCandidates(
|
|
101
|
+
dirToCommunities: Map<string, Set<number>>,
|
|
102
|
+
): DriftResult['splitCandidates'] {
|
|
103
|
+
const candidates: DriftResult['splitCandidates'] = [];
|
|
102
104
|
for (const [dir, cids] of dirToCommunities) {
|
|
103
105
|
if (cids.size >= 2) {
|
|
104
|
-
|
|
106
|
+
candidates.push({ directory: dir, communityCount: cids.size });
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
|
-
|
|
109
|
+
candidates.sort((a, b) => b.communityCount - a.communityCount);
|
|
110
|
+
return candidates;
|
|
111
|
+
}
|
|
108
112
|
|
|
109
|
-
|
|
113
|
+
function findMergeCandidates(communities: CommunityObject[]): DriftResult['mergeCandidates'] {
|
|
114
|
+
const candidates: DriftResult['mergeCandidates'] = [];
|
|
110
115
|
for (const c of communities) {
|
|
111
116
|
const dirCount = Object.keys(c.directories).length;
|
|
112
117
|
if (dirCount >= 2) {
|
|
113
|
-
|
|
118
|
+
candidates.push({
|
|
114
119
|
communityId: c.id,
|
|
115
120
|
size: c.size,
|
|
116
121
|
directoryCount: dirCount,
|
|
@@ -118,7 +123,17 @@ function analyzeDrift(
|
|
|
118
123
|
});
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
|
-
|
|
126
|
+
candidates.sort((a, b) => b.directoryCount - a.directoryCount);
|
|
127
|
+
return candidates;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function analyzeDrift(
|
|
131
|
+
communities: CommunityObject[],
|
|
132
|
+
communityDirs: Map<number, Set<string>>,
|
|
133
|
+
): DriftResult {
|
|
134
|
+
const dirToCommunities = buildDirToCommunities(communityDirs);
|
|
135
|
+
const splitCandidates = findSplitCandidates(dirToCommunities);
|
|
136
|
+
const mergeCandidates = findMergeCandidates(communities);
|
|
122
137
|
|
|
123
138
|
const totalDirs = dirToCommunities.size;
|
|
124
139
|
const splitRatio = totalDirs > 0 ? splitCandidates.length / totalDirs : 0;
|