@optave/codegraph 3.4.0 → 3.4.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 +7 -7
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +3 -9
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +0 -1
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitors/cfg-conditionals.d.ts +5 -0
- package/dist/ast-analysis/visitors/cfg-conditionals.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/cfg-conditionals.js +166 -0
- package/dist/ast-analysis/visitors/cfg-conditionals.js.map +1 -0
- package/dist/ast-analysis/visitors/cfg-loops.d.ts +7 -0
- package/dist/ast-analysis/visitors/cfg-loops.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/cfg-loops.js +73 -0
- package/dist/ast-analysis/visitors/cfg-loops.js.map +1 -0
- package/dist/ast-analysis/visitors/cfg-shared.d.ts +56 -0
- package/dist/ast-analysis/visitors/cfg-shared.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/cfg-shared.js +107 -0
- package/dist/ast-analysis/visitors/cfg-shared.js.map +1 -0
- package/dist/ast-analysis/visitors/cfg-try-catch.d.ts +4 -0
- package/dist/ast-analysis/visitors/cfg-try-catch.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/cfg-try-catch.js +100 -0
- package/dist/ast-analysis/visitors/cfg-try-catch.js.map +1 -0
- package/dist/ast-analysis/visitors/cfg-visitor.d.ts +2 -2
- package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/cfg-visitor.js +11 -445
- package/dist/ast-analysis/visitors/cfg-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.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/batch.d.ts.map +1 -1
- package/dist/cli/commands/batch.js +4 -3
- package/dist/cli/commands/batch.js.map +1 -1
- package/dist/cli/commands/branch-compare.js +1 -1
- package/dist/cli/commands/branch-compare.js.map +1 -1
- package/dist/cli/commands/build.js +1 -1
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +1 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/path.d.ts.map +1 -1
- package/dist/cli/commands/path.js +7 -2
- package/dist/cli/commands/path.js.map +1 -1
- package/dist/cli/commands/plot.d.ts.map +1 -1
- package/dist/cli/commands/plot.js +2 -2
- package/dist/cli/commands/plot.js.map +1 -1
- package/dist/cli/commands/watch.js +1 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/shared/open-graph.d.ts +2 -2
- package/dist/cli/shared/open-graph.d.ts.map +1 -1
- package/dist/cli/shared/open-graph.js.map +1 -1
- package/dist/cli/types.d.ts +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +2 -3
- package/dist/cli.js.map +1 -1
- package/dist/db/connection.d.ts +17 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +91 -2
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +7 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +1 -3
- 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 +2 -4
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts +49 -0
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +145 -0
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/diff-impact.d.ts +76 -0
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -0
- package/dist/domain/analysis/diff-impact.js +282 -0
- package/dist/domain/analysis/diff-impact.js.map +1 -0
- package/dist/domain/analysis/exports.d.ts.map +1 -1
- package/dist/domain/analysis/exports.js +0 -1
- package/dist/domain/analysis/exports.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +66 -0
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -0
- package/dist/domain/analysis/fn-impact.js +189 -0
- package/dist/domain/analysis/fn-impact.js.map +1 -0
- package/dist/domain/analysis/impact.d.ts +8 -148
- package/dist/domain/analysis/impact.d.ts.map +1 -1
- package/dist/domain/analysis/impact.js +8 -568
- package/dist/domain/analysis/impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +1 -3
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/context.d.ts +2 -3
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +4 -5
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +1 -2
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts +2 -3
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +6 -0
- 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 +12 -2
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +155 -59
- package/dist/domain/graph/builder/stages/build-structure.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 +6 -6
- 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 +85 -61
- 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.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 +58 -11
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.js +2 -2
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.d.ts.map +1 -1
- package/dist/domain/graph/resolve.js +10 -8
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +1 -3
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +11 -12
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/queries.d.ts +3 -2
- package/dist/domain/queries.d.ts.map +1 -1
- package/dist/domain/queries.js +3 -2
- package/dist/domain/queries.js.map +1 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/extractors/csharp.js +2 -2
- package/dist/extractors/csharp.js.map +1 -1
- package/dist/extractors/go.js +2 -2
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts +5 -0
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +5 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/javascript.js +58 -60
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/php.js +2 -2
- package/dist/extractors/php.js.map +1 -1
- package/dist/extractors/python.js +2 -2
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +2 -2
- package/dist/extractors/rust.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +1 -2
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +2 -3
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +2 -4
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/cochange.js +4 -4
- package/dist/features/cochange.js.map +1 -1
- package/dist/features/communities.js +4 -4
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity-query.d.ts +37 -0
- package/dist/features/complexity-query.d.ts.map +1 -0
- package/dist/features/complexity-query.js +263 -0
- package/dist/features/complexity-query.js.map +1 -0
- package/dist/features/complexity.d.ts +2 -30
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +7 -261
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +8 -24
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/export.d.ts +7 -8
- package/dist/features/export.d.ts.map +1 -1
- package/dist/features/export.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +1 -3
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/manifesto.js +8 -8
- package/dist/features/manifesto.js.map +1 -1
- package/dist/features/snapshot.d.ts.map +1 -1
- package/dist/features/snapshot.js +0 -1
- package/dist/features/snapshot.js.map +1 -1
- package/dist/features/structure-query.d.ts +76 -0
- package/dist/features/structure-query.d.ts.map +1 -0
- package/dist/features/structure-query.js +245 -0
- package/dist/features/structure-query.js.map +1 -0
- package/dist/features/structure.d.ts +12 -67
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +188 -244
- package/dist/features/structure.js.map +1 -1
- package/dist/features/triage.js +2 -2
- package/dist/features/triage.js.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.js +2 -9
- package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts +5 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +20 -12
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +12 -11
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/native.d.ts.map +1 -1
- package/dist/infrastructure/native.js +7 -3
- package/dist/infrastructure/native.js.map +1 -1
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +1 -1
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/infrastructure/update-check.js +3 -3
- package/dist/infrastructure/update-check.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -8
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +9 -4
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools/audit.js +1 -1
- package/dist/mcp/tools/audit.js.map +1 -1
- package/dist/mcp/tools/cfg.js +1 -1
- package/dist/mcp/tools/cfg.js.map +1 -1
- package/dist/mcp/tools/check.js +2 -2
- package/dist/mcp/tools/check.js.map +1 -1
- package/dist/mcp/tools/dataflow.js +2 -2
- package/dist/mcp/tools/dataflow.js.map +1 -1
- package/dist/mcp/tools/export-graph.js +1 -1
- package/dist/mcp/tools/export-graph.js.map +1 -1
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/path.d.ts +1 -0
- package/dist/mcp/tools/path.d.ts.map +1 -1
- package/dist/mcp/tools/path.js +9 -0
- package/dist/mcp/tools/path.js.map +1 -1
- package/dist/mcp/tools/query.js +1 -1
- package/dist/mcp/tools/query.js.map +1 -1
- package/dist/mcp/tools/semantic-search.js +1 -1
- package/dist/mcp/tools/semantic-search.js.map +1 -1
- package/dist/mcp/tools/sequence.js +1 -1
- package/dist/mcp/tools/sequence.js.map +1 -1
- package/dist/mcp/tools/symbol-children.js +1 -1
- package/dist/mcp/tools/symbol-children.js.map +1 -1
- package/dist/mcp/tools/triage.js +1 -1
- package/dist/mcp/tools/triage.js.map +1 -1
- package/dist/presentation/audit.d.ts.map +1 -1
- package/dist/presentation/audit.js +0 -1
- package/dist/presentation/audit.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts +11 -0
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -0
- package/dist/presentation/diff-impact-mermaid.js +105 -0
- package/dist/presentation/diff-impact-mermaid.js.map +1 -0
- package/dist/presentation/flow.d.ts.map +1 -1
- package/dist/presentation/flow.js +0 -2
- package/dist/presentation/flow.js.map +1 -1
- package/dist/presentation/manifesto.d.ts.map +1 -1
- package/dist/presentation/manifesto.js +0 -1
- package/dist/presentation/manifesto.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/path.d.ts.map +1 -1
- package/dist/presentation/queries-cli/path.js +45 -1
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +1 -3
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/sequence.d.ts.map +1 -1
- package/dist/presentation/sequence.js +0 -1
- package/dist/presentation/sequence.js.map +1 -1
- package/dist/presentation/structure.d.ts.map +1 -1
- package/dist/presentation/structure.js.map +1 -1
- package/dist/presentation/triage.d.ts.map +1 -1
- package/dist/presentation/triage.js +0 -1
- package/dist/presentation/triage.js.map +1 -1
- package/dist/shared/constants.d.ts +9 -3
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +6 -3
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +4 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/version.d.ts +2 -0
- package/dist/shared/version.d.ts.map +1 -0
- package/dist/shared/version.js +5 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/ast-analysis/engine.ts +3 -9
- package/src/ast-analysis/shared.ts +0 -1
- package/src/ast-analysis/visitors/cfg-conditionals.ts +227 -0
- package/src/ast-analysis/visitors/cfg-loops.ts +136 -0
- package/src/ast-analysis/visitors/cfg-shared.ts +196 -0
- package/src/ast-analysis/visitors/cfg-try-catch.ts +142 -0
- package/src/ast-analysis/visitors/cfg-visitor.ts +34 -655
- package/src/ast-analysis/visitors/complexity-visitor.ts +0 -1
- package/src/ast-analysis/visitors/dataflow-visitor.ts +0 -1
- package/src/cli/commands/batch.ts +4 -3
- package/src/cli/commands/branch-compare.ts +1 -1
- package/src/cli/commands/build.ts +1 -1
- package/src/cli/commands/info.ts +1 -2
- package/src/cli/commands/path.ts +7 -2
- package/src/cli/commands/plot.ts +2 -2
- package/src/cli/commands/watch.ts +1 -1
- package/src/cli/index.ts +2 -2
- package/src/cli/shared/open-graph.ts +2 -2
- package/src/cli/types.ts +1 -1
- package/src/cli.ts +2 -3
- package/src/db/connection.ts +97 -13
- package/src/db/index.ts +2 -0
- package/src/db/migrations.ts +7 -0
- package/src/domain/analysis/brief.ts +0 -1
- package/src/domain/analysis/context.ts +2 -6
- package/src/domain/analysis/dependencies.ts +165 -0
- package/src/domain/analysis/diff-impact.ts +354 -0
- package/src/domain/analysis/exports.ts +0 -2
- package/src/domain/analysis/fn-impact.ts +241 -0
- package/src/domain/analysis/impact.ts +8 -718
- package/src/domain/analysis/module-map.ts +1 -5
- package/src/domain/graph/builder/context.ts +2 -2
- package/src/domain/graph/builder/helpers.ts +14 -11
- package/src/domain/graph/builder/incremental.ts +33 -28
- package/src/domain/graph/builder/pipeline.ts +8 -0
- package/src/domain/graph/builder/stages/build-edges.ts +17 -4
- package/src/domain/graph/builder/stages/build-structure.ts +205 -76
- package/src/domain/graph/builder/stages/detect-changes.ts +11 -12
- package/src/domain/graph/builder/stages/finalize.ts +100 -81
- package/src/domain/graph/builder/stages/insert-nodes.ts +12 -8
- package/src/domain/graph/builder/stages/resolve-imports.ts +75 -10
- package/src/domain/graph/cycles.ts +2 -2
- package/src/domain/graph/resolve.ts +14 -8
- package/src/domain/graph/watcher.ts +2 -4
- package/src/domain/parser.ts +11 -13
- package/src/domain/queries.ts +2 -2
- package/src/domain/search/generator.ts +3 -4
- package/src/extractors/csharp.ts +2 -2
- package/src/extractors/go.ts +2 -2
- package/src/extractors/helpers.ts +6 -0
- package/src/extractors/javascript.ts +58 -61
- package/src/extractors/php.ts +2 -2
- package/src/extractors/python.ts +2 -2
- package/src/extractors/rust.ts +2 -2
- package/src/features/audit.ts +1 -2
- package/src/features/branch-compare.ts +3 -9
- package/src/features/cfg.ts +2 -4
- package/src/features/cochange.ts +4 -4
- package/src/features/communities.ts +4 -4
- package/src/features/complexity-query.ts +370 -0
- package/src/features/complexity.ts +6 -365
- package/src/features/dataflow.ts +48 -70
- package/src/features/export.ts +12 -16
- package/src/features/flow.ts +0 -1
- package/src/features/graph-enrichment.ts +1 -3
- package/src/features/manifesto.ts +8 -8
- package/src/features/snapshot.ts +1 -2
- package/src/features/structure-query.ts +387 -0
- package/src/features/structure.ts +231 -376
- package/src/features/triage.ts +2 -2
- package/src/graph/algorithms/leiden/adapter.ts +2 -9
- package/src/graph/classifiers/roles.ts +22 -13
- package/src/index.ts +1 -0
- package/src/infrastructure/config.ts +12 -13
- package/src/infrastructure/native.ts +7 -3
- package/src/infrastructure/registry.ts +1 -1
- package/src/infrastructure/update-check.ts +3 -3
- package/src/mcp/server.ts +2 -10
- package/src/mcp/tool-registry.ts +11 -4
- package/src/mcp/tools/audit.ts +1 -1
- package/src/mcp/tools/cfg.ts +1 -1
- package/src/mcp/tools/check.ts +2 -2
- package/src/mcp/tools/dataflow.ts +2 -2
- package/src/mcp/tools/export-graph.ts +1 -1
- package/src/mcp/tools/index.ts +0 -1
- package/src/mcp/tools/path.ts +10 -0
- package/src/mcp/tools/query.ts +1 -1
- package/src/mcp/tools/semantic-search.ts +1 -1
- package/src/mcp/tools/sequence.ts +1 -1
- package/src/mcp/tools/symbol-children.ts +1 -1
- package/src/mcp/tools/triage.ts +1 -1
- package/src/presentation/audit.ts +0 -1
- package/src/presentation/diff-impact-mermaid.ts +127 -0
- package/src/presentation/flow.ts +0 -2
- package/src/presentation/manifesto.ts +0 -1
- package/src/presentation/queries-cli/inspect.ts +0 -1
- package/src/presentation/queries-cli/path.ts +71 -1
- package/src/presentation/result-formatter.ts +0 -1
- package/src/presentation/sequence.ts +0 -1
- package/src/presentation/structure.ts +0 -12
- package/src/presentation/triage.ts +0 -1
- package/src/shared/constants.ts +33 -19
- package/src/shared/errors.ts +5 -0
- package/src/shared/version.ts +10 -0
- package/src/types.ts +4 -10
- package/src/vendor.d.ts +0 -39
|
@@ -380,11 +380,7 @@ export function moduleMapData(customDbPath: string, limit = 20, opts: { noTests?
|
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
export function statsData(
|
|
384
|
-
customDbPath: string,
|
|
385
|
-
// biome-ignore lint/suspicious/noExplicitAny: config shape is dynamic
|
|
386
|
-
opts: { noTests?: boolean; config?: any } = {},
|
|
387
|
-
) {
|
|
383
|
+
export function statsData(customDbPath: string, opts: { noTests?: boolean; config?: any } = {}) {
|
|
388
384
|
const db = openReadonlyOrFail(customDbPath);
|
|
389
385
|
try {
|
|
390
386
|
const noTests = opts.noTests || false;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Each stage reads what it needs and writes what it produces.
|
|
5
5
|
* This replaces the closure-captured locals in the old monolithic buildGraph().
|
|
6
6
|
*/
|
|
7
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
8
7
|
import type {
|
|
8
|
+
BetterSqlite3Database,
|
|
9
9
|
BuildGraphOpts,
|
|
10
10
|
CodegraphConfig,
|
|
11
11
|
EngineOpts,
|
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
export class PipelineContext {
|
|
21
21
|
// ── Inputs (set during setup) ──────────────────────────────────────
|
|
22
22
|
rootDir!: string;
|
|
23
|
-
db!:
|
|
23
|
+
db!: BetterSqlite3Database;
|
|
24
24
|
dbPath!: string;
|
|
25
25
|
config!: CodegraphConfig;
|
|
26
26
|
opts!: BuildGraphOpts;
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
10
9
|
import { purgeFilesData } from '../../../db/index.js';
|
|
11
10
|
import { warn } from '../../../infrastructure/logger.js';
|
|
12
11
|
import { EXTENSIONS, IGNORE_DIRS } from '../../../shared/constants.js';
|
|
13
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
BetterSqlite3Database,
|
|
14
|
+
CodegraphConfig,
|
|
15
|
+
PathAliases,
|
|
16
|
+
SqliteStatement,
|
|
17
|
+
} from '../../../types.js';
|
|
14
18
|
|
|
15
19
|
export const BUILTIN_RECEIVERS: Set<string> = new Set([
|
|
16
20
|
'console',
|
|
@@ -132,8 +136,7 @@ export function loadPathAliases(rootDir: string): PathAliases {
|
|
|
132
136
|
try {
|
|
133
137
|
const raw = fs
|
|
134
138
|
.readFileSync(configPath, 'utf-8')
|
|
135
|
-
.replace(
|
|
136
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
139
|
+
.replace(/("(?:[^"\\]|\\.)*")|\/\*[\s\S]*?\*\/|\/\/.*$/gm, (_, str) => str ?? '')
|
|
137
140
|
.replace(/,\s*([\]}])/g, '$1');
|
|
138
141
|
const config = JSON.parse(raw) as {
|
|
139
142
|
compilerOptions?: { baseUrl?: string; paths?: Record<string, string[]> };
|
|
@@ -199,7 +202,7 @@ export function readFileSafe(filePath: string, retries: number = 2): string {
|
|
|
199
202
|
* Purge all graph data for the specified files.
|
|
200
203
|
*/
|
|
201
204
|
export function purgeFilesFromGraph(
|
|
202
|
-
db:
|
|
205
|
+
db: BetterSqlite3Database,
|
|
203
206
|
files: string[],
|
|
204
207
|
options: Record<string, unknown> = {},
|
|
205
208
|
): void {
|
|
@@ -211,10 +214,10 @@ export function purgeFilesFromGraph(
|
|
|
211
214
|
const BATCH_CHUNK = 500;
|
|
212
215
|
|
|
213
216
|
// Statement caches keyed by chunk size — avoids recompiling for every batch.
|
|
214
|
-
const nodeStmtCache = new WeakMap<
|
|
215
|
-
const edgeStmtCache = new WeakMap<
|
|
217
|
+
const nodeStmtCache = new WeakMap<BetterSqlite3Database, Map<number, SqliteStatement>>();
|
|
218
|
+
const edgeStmtCache = new WeakMap<BetterSqlite3Database, Map<number, SqliteStatement>>();
|
|
216
219
|
|
|
217
|
-
function getNodeStmt(db:
|
|
220
|
+
function getNodeStmt(db: BetterSqlite3Database, chunkSize: number): SqliteStatement {
|
|
218
221
|
let cache = nodeStmtCache.get(db);
|
|
219
222
|
if (!cache) {
|
|
220
223
|
cache = new Map();
|
|
@@ -232,7 +235,7 @@ function getNodeStmt(db: BetterSqlite3.Database, chunkSize: number): BetterSqlit
|
|
|
232
235
|
return stmt;
|
|
233
236
|
}
|
|
234
237
|
|
|
235
|
-
function getEdgeStmt(db:
|
|
238
|
+
function getEdgeStmt(db: BetterSqlite3Database, chunkSize: number): SqliteStatement {
|
|
236
239
|
let cache = edgeStmtCache.get(db);
|
|
237
240
|
if (!cache) {
|
|
238
241
|
cache = new Map();
|
|
@@ -254,7 +257,7 @@ function getEdgeStmt(db: BetterSqlite3.Database, chunkSize: number): BetterSqlit
|
|
|
254
257
|
* Batch-insert node rows via multi-value INSERT statements.
|
|
255
258
|
* Each row: [name, kind, file, line, end_line, parent_id, qualified_name, scope, visibility]
|
|
256
259
|
*/
|
|
257
|
-
export function batchInsertNodes(db:
|
|
260
|
+
export function batchInsertNodes(db: BetterSqlite3Database, rows: unknown[][]): void {
|
|
258
261
|
if (!rows.length) return;
|
|
259
262
|
for (let i = 0; i < rows.length; i += BATCH_CHUNK) {
|
|
260
263
|
const end = Math.min(i + BATCH_CHUNK, rows.length);
|
|
@@ -273,7 +276,7 @@ export function batchInsertNodes(db: BetterSqlite3.Database, rows: unknown[][]):
|
|
|
273
276
|
* Batch-insert edge rows via multi-value INSERT statements.
|
|
274
277
|
* Each row: [source_id, target_id, kind, confidence, dynamic]
|
|
275
278
|
*/
|
|
276
|
-
export function batchInsertEdges(db:
|
|
279
|
+
export function batchInsertEdges(db: BetterSqlite3Database, rows: unknown[][]): void {
|
|
277
280
|
if (!rows.length) return;
|
|
278
281
|
for (let i = 0; i < rows.length; i += BATCH_CHUNK) {
|
|
279
282
|
const end = Math.min(i + BATCH_CHUNK, rows.length);
|
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
13
12
|
import { bulkNodeIdsByFile } from '../../../db/index.js';
|
|
14
13
|
import { warn } from '../../../infrastructure/logger.js';
|
|
15
14
|
import { normalizePath } from '../../../shared/constants.js';
|
|
16
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
BetterSqlite3Database,
|
|
17
|
+
EngineOpts,
|
|
18
|
+
ExtractorOutput,
|
|
19
|
+
PathAliases,
|
|
20
|
+
SqliteStatement,
|
|
21
|
+
} from '../../../types.js';
|
|
17
22
|
import { parseFileIncremental } from '../../parser.js';
|
|
18
23
|
import { computeConfidence, resolveImportPath } from '../resolve.js';
|
|
19
24
|
import { BUILTIN_RECEIVERS, readFileSafe } from './helpers.js';
|
|
@@ -64,7 +69,7 @@ function insertFileNodes(stmts: IncrementalStmts, relPath: string, symbols: Extr
|
|
|
64
69
|
// ── Containment edges ──────────────────────────────────────────────────
|
|
65
70
|
|
|
66
71
|
function buildContainmentEdges(
|
|
67
|
-
db:
|
|
72
|
+
db: BetterSqlite3Database,
|
|
68
73
|
stmts: IncrementalStmts,
|
|
69
74
|
relPath: string,
|
|
70
75
|
symbols: ExtractorOutput,
|
|
@@ -101,13 +106,13 @@ function buildContainmentEdges(
|
|
|
101
106
|
// ── Reverse-dep cascade ────────────────────────────────────────────────
|
|
102
107
|
|
|
103
108
|
// Lazily-cached prepared statements for reverse-dep operations
|
|
104
|
-
let _revDepDb:
|
|
105
|
-
let _findRevDepsStmt:
|
|
106
|
-
let _deleteOutEdgesStmt:
|
|
109
|
+
let _revDepDb: BetterSqlite3Database | null = null;
|
|
110
|
+
let _findRevDepsStmt: SqliteStatement | null = null;
|
|
111
|
+
let _deleteOutEdgesStmt: SqliteStatement | null = null;
|
|
107
112
|
|
|
108
|
-
function getRevDepStmts(db:
|
|
109
|
-
findRevDepsStmt:
|
|
110
|
-
deleteOutEdgesStmt:
|
|
113
|
+
function getRevDepStmts(db: BetterSqlite3Database): {
|
|
114
|
+
findRevDepsStmt: SqliteStatement;
|
|
115
|
+
deleteOutEdgesStmt: SqliteStatement;
|
|
111
116
|
} {
|
|
112
117
|
if (_revDepDb !== db) {
|
|
113
118
|
_revDepDb = db;
|
|
@@ -127,12 +132,12 @@ function getRevDepStmts(db: BetterSqlite3.Database): {
|
|
|
127
132
|
};
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
function findReverseDeps(db:
|
|
135
|
+
function findReverseDeps(db: BetterSqlite3Database, relPath: string): string[] {
|
|
131
136
|
const { findRevDepsStmt } = getRevDepStmts(db);
|
|
132
137
|
return (findRevDepsStmt.all(relPath, relPath) as Array<{ file: string }>).map((r) => r.file);
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
function deleteOutgoingEdges(db:
|
|
140
|
+
function deleteOutgoingEdges(db: BetterSqlite3Database, relPath: string): void {
|
|
136
141
|
const { deleteOutEdgesStmt } = getRevDepStmts(db);
|
|
137
142
|
deleteOutEdgesStmt.run(relPath);
|
|
138
143
|
}
|
|
@@ -157,7 +162,7 @@ async function parseReverseDep(
|
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
function rebuildReverseDepEdges(
|
|
160
|
-
db:
|
|
165
|
+
db: BetterSqlite3Database,
|
|
161
166
|
rootDir: string,
|
|
162
167
|
depRelPath: string,
|
|
163
168
|
symbols: ExtractorOutput,
|
|
@@ -187,7 +192,7 @@ function rebuildReverseDepEdges(
|
|
|
187
192
|
// ── Directory containment edges ────────────────────────────────────────
|
|
188
193
|
|
|
189
194
|
function rebuildDirContainment(
|
|
190
|
-
_db:
|
|
195
|
+
_db: BetterSqlite3Database,
|
|
191
196
|
stmts: IncrementalStmts,
|
|
192
197
|
relPath: string,
|
|
193
198
|
): number {
|
|
@@ -204,7 +209,7 @@ function rebuildDirContainment(
|
|
|
204
209
|
|
|
205
210
|
// ── Ancillary table cleanup ────────────────────────────────────────────
|
|
206
211
|
|
|
207
|
-
function purgeAncillaryData(db:
|
|
212
|
+
function purgeAncillaryData(db: BetterSqlite3Database, relPath: string): void {
|
|
208
213
|
const tryExec = (sql: string, ...args: string[]): void => {
|
|
209
214
|
try {
|
|
210
215
|
db.prepare(sql).run(...args);
|
|
@@ -239,15 +244,15 @@ function purgeAncillaryData(db: BetterSqlite3.Database, relPath: string): void {
|
|
|
239
244
|
// ── Import edge building ────────────────────────────────────────────────
|
|
240
245
|
|
|
241
246
|
// Lazily-cached prepared statements for barrel resolution (avoid re-preparing in hot loops)
|
|
242
|
-
let _barrelDb:
|
|
243
|
-
let _isBarrelStmt:
|
|
244
|
-
let _reexportTargetsStmt:
|
|
245
|
-
let _hasDefStmt:
|
|
246
|
-
|
|
247
|
-
function getBarrelStmts(db:
|
|
248
|
-
isBarrelStmt:
|
|
249
|
-
reexportTargetsStmt:
|
|
250
|
-
hasDefStmt:
|
|
247
|
+
let _barrelDb: BetterSqlite3Database | null = null;
|
|
248
|
+
let _isBarrelStmt: SqliteStatement | null = null;
|
|
249
|
+
let _reexportTargetsStmt: SqliteStatement | null = null;
|
|
250
|
+
let _hasDefStmt: SqliteStatement | null = null;
|
|
251
|
+
|
|
252
|
+
function getBarrelStmts(db: BetterSqlite3Database): {
|
|
253
|
+
isBarrelStmt: SqliteStatement;
|
|
254
|
+
reexportTargetsStmt: SqliteStatement;
|
|
255
|
+
hasDefStmt: SqliteStatement;
|
|
251
256
|
} {
|
|
252
257
|
if (_barrelDb !== db) {
|
|
253
258
|
_barrelDb = db;
|
|
@@ -273,14 +278,14 @@ function getBarrelStmts(db: BetterSqlite3.Database): {
|
|
|
273
278
|
};
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
function isBarrelFile(db:
|
|
281
|
+
function isBarrelFile(db: BetterSqlite3Database, relPath: string): boolean {
|
|
277
282
|
const { isBarrelStmt } = getBarrelStmts(db);
|
|
278
283
|
const reexportCount = (isBarrelStmt.get(relPath) as { c: number } | undefined)?.c;
|
|
279
284
|
return (reexportCount || 0) > 0;
|
|
280
285
|
}
|
|
281
286
|
|
|
282
287
|
function resolveBarrelTarget(
|
|
283
|
-
db:
|
|
288
|
+
db: BetterSqlite3Database,
|
|
284
289
|
barrelPath: string,
|
|
285
290
|
symbolName: string,
|
|
286
291
|
visited: Set<string> = new Set(),
|
|
@@ -312,7 +317,7 @@ function resolveBarrelTarget(
|
|
|
312
317
|
* Shared by buildImportEdges (primary file) and Pass 2 of the reverse-dep cascade.
|
|
313
318
|
*/
|
|
314
319
|
function resolveBarrelImportEdges(
|
|
315
|
-
db:
|
|
320
|
+
db: BetterSqlite3Database,
|
|
316
321
|
stmts: IncrementalStmts,
|
|
317
322
|
fileNodeId: number,
|
|
318
323
|
resolvedPath: string,
|
|
@@ -344,7 +349,7 @@ function buildImportEdges(
|
|
|
344
349
|
rootDir: string,
|
|
345
350
|
fileNodeId: number,
|
|
346
351
|
aliases: PathAliases,
|
|
347
|
-
db:
|
|
352
|
+
db: BetterSqlite3Database | null,
|
|
348
353
|
): number {
|
|
349
354
|
let edgesAdded = 0;
|
|
350
355
|
for (const imp of symbols.imports) {
|
|
@@ -504,7 +509,7 @@ function buildCallEdges(
|
|
|
504
509
|
* Parse a single file and update the database incrementally.
|
|
505
510
|
*/
|
|
506
511
|
export async function rebuildFile(
|
|
507
|
-
db:
|
|
512
|
+
db: BetterSqlite3Database,
|
|
508
513
|
rootDir: string,
|
|
509
514
|
filePath: string,
|
|
510
515
|
stmts: IncrementalStmts,
|
|
@@ -9,6 +9,7 @@ import { performance } from 'node:perf_hooks';
|
|
|
9
9
|
import { closeDb, getBuildMeta, initSchema, MIGRATIONS, openDb } from '../../../db/index.js';
|
|
10
10
|
import { detectWorkspaces, loadConfig } from '../../../infrastructure/config.js';
|
|
11
11
|
import { info, warn } from '../../../infrastructure/logger.js';
|
|
12
|
+
import { CODEGRAPH_VERSION } from '../../../shared/version.js';
|
|
12
13
|
import type { BuildGraphOpts, BuildResult } from '../../../types.js';
|
|
13
14
|
import { getActiveEngine } from '../../parser.js';
|
|
14
15
|
import { setWorkspaces } from '../resolve.js';
|
|
@@ -57,6 +58,13 @@ function checkEngineSchemaMismatch(ctx: PipelineContext): void {
|
|
|
57
58
|
);
|
|
58
59
|
ctx.forceFullRebuild = true;
|
|
59
60
|
}
|
|
61
|
+
const prevVersion = getBuildMeta(ctx.db, 'codegraph_version');
|
|
62
|
+
if (prevVersion && prevVersion !== CODEGRAPH_VERSION) {
|
|
63
|
+
info(
|
|
64
|
+
`Codegraph version changed (${prevVersion} → ${CODEGRAPH_VERSION}), promoting to full rebuild.`,
|
|
65
|
+
);
|
|
66
|
+
ctx.forceFullRebuild = true;
|
|
67
|
+
}
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
function loadAliases(ctx: PipelineContext): void {
|
|
@@ -6,13 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { performance } from 'node:perf_hooks';
|
|
9
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
10
9
|
import { getNodeId } from '../../../../db/index.js';
|
|
11
10
|
import { loadNative } from '../../../../infrastructure/native.js';
|
|
12
11
|
import type {
|
|
12
|
+
BetterSqlite3Database,
|
|
13
13
|
Call,
|
|
14
14
|
ClassRelation,
|
|
15
|
-
Definition,
|
|
16
15
|
ExtractorOutput,
|
|
17
16
|
Import,
|
|
18
17
|
NativeAddon,
|
|
@@ -69,7 +68,7 @@ interface NormalizedTypeEntry {
|
|
|
69
68
|
|
|
70
69
|
// ── Node lookup setup ───────────────────────────────────────────────────
|
|
71
70
|
|
|
72
|
-
function makeGetNodeIdStmt(db:
|
|
71
|
+
function makeGetNodeIdStmt(db: BetterSqlite3Database): NodeIdStmt {
|
|
73
72
|
return {
|
|
74
73
|
get: (name: string, kind: string, file: string, line: number) => {
|
|
75
74
|
const id = getNodeId(db, name, kind, file, line);
|
|
@@ -102,12 +101,15 @@ function buildImportEdges(
|
|
|
102
101
|
const { fileSymbols, barrelOnlyFiles, rootDir } = ctx;
|
|
103
102
|
|
|
104
103
|
for (const [relPath, symbols] of fileSymbols) {
|
|
105
|
-
|
|
104
|
+
const isBarrelOnly = barrelOnlyFiles.has(relPath);
|
|
106
105
|
const fileNodeRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
107
106
|
if (!fileNodeRow) continue;
|
|
108
107
|
const fileNodeId = fileNodeRow.id;
|
|
109
108
|
|
|
110
109
|
for (const imp of symbols.imports) {
|
|
110
|
+
// Barrel-only files: only emit reexport edges, skip regular imports
|
|
111
|
+
if (isBarrelOnly && !imp.reexport) continue;
|
|
112
|
+
|
|
111
113
|
const resolvedPath = getResolved(ctx, path.join(rootDir, relPath), imp.source);
|
|
112
114
|
const targetRow = getNodeIdStmt.get(resolvedPath, 'file', resolvedPath, 0);
|
|
113
115
|
if (!targetRow) continue;
|
|
@@ -573,6 +575,17 @@ export async function buildEdges(ctx: PipelineContext): Promise<void> {
|
|
|
573
575
|
|
|
574
576
|
const t0 = performance.now();
|
|
575
577
|
const buildEdgesTx = db.transaction(() => {
|
|
578
|
+
// Delete stale outgoing edges for barrel-only files inside the transaction
|
|
579
|
+
// so that deletion and re-creation are atomic (no edge loss on mid-build crash).
|
|
580
|
+
if (ctx.barrelOnlyFiles.size > 0) {
|
|
581
|
+
const deleteOutgoingEdges = db.prepare(
|
|
582
|
+
'DELETE FROM edges WHERE source_id IN (SELECT id FROM nodes WHERE file = ?)',
|
|
583
|
+
);
|
|
584
|
+
for (const relPath of ctx.barrelOnlyFiles) {
|
|
585
|
+
deleteOutgoingEdges.run(relPath);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
576
589
|
const allEdgeRows: EdgeRowTuple[] = [];
|
|
577
590
|
|
|
578
591
|
buildImportEdges(ctx, getNodeIdStmt, allEdgeRows);
|
|
@@ -32,94 +32,71 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
);
|
|
54
|
-
const cachedLineCounts = new Map<string, number>();
|
|
55
|
-
for (const row of lineCountByFile.all() as Array<{ file: string; line_count: number }>) {
|
|
56
|
-
cachedLineCounts.set(row.file, row.line_count);
|
|
57
|
-
}
|
|
58
|
-
let loadedFromDb = 0;
|
|
59
|
-
for (const { file: relPath } of existingFiles) {
|
|
60
|
-
if (!fileSymbols.has(relPath)) {
|
|
61
|
-
const importCount =
|
|
62
|
-
(importCountByFile.get(relPath) as { cnt: number } | undefined)?.cnt || 0;
|
|
63
|
-
fileSymbols.set(relPath, {
|
|
64
|
-
definitions: defsByFile.all(relPath),
|
|
65
|
-
imports: new Array(importCount) as unknown as ExtractorOutput['imports'],
|
|
66
|
-
exports: [],
|
|
67
|
-
} as unknown as ExtractorOutput);
|
|
68
|
-
loadedFromDb++;
|
|
69
|
-
}
|
|
70
|
-
if (!ctx.lineCountMap.has(relPath)) {
|
|
71
|
-
const cached = cachedLineCounts.get(relPath);
|
|
72
|
-
if (cached != null) {
|
|
73
|
-
ctx.lineCountMap.set(relPath, cached);
|
|
74
|
-
} else {
|
|
75
|
-
const absPath = path.join(rootDir, relPath);
|
|
76
|
-
try {
|
|
77
|
-
const content = readFileSafe(absPath);
|
|
78
|
-
ctx.lineCountMap.set(relPath, content.split('\n').length);
|
|
79
|
-
} catch {
|
|
80
|
-
ctx.lineCountMap.set(relPath, 0);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
debug(`Structure: ${fileSymbols.size} files (${loadedFromDb} loaded from DB)`);
|
|
35
|
+
const changedFileList = isFullBuild ? null : [...allSymbols.keys()];
|
|
36
|
+
|
|
37
|
+
// For small incremental builds on large codebases, use a fast path that
|
|
38
|
+
// updates only the changed files' metrics via targeted SQL instead of
|
|
39
|
+
// loading ALL definitions from DB (~8ms) and recomputing ALL metrics (~15ms).
|
|
40
|
+
// Gate: ≤5 changed files AND significantly more existing files (>20) to
|
|
41
|
+
// avoid triggering on small test fixtures where directory metrics matter.
|
|
42
|
+
const existingFileCount = !isFullBuild
|
|
43
|
+
? (db.prepare("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'").get() as { c: number }).c
|
|
44
|
+
: 0;
|
|
45
|
+
const useSmallIncrementalFastPath =
|
|
46
|
+
!isFullBuild &&
|
|
47
|
+
changedFileList != null &&
|
|
48
|
+
changedFileList.length <= 5 &&
|
|
49
|
+
existingFileCount > 20;
|
|
50
|
+
|
|
51
|
+
if (!isFullBuild && !useSmallIncrementalFastPath) {
|
|
52
|
+
// Medium/large incremental: load unchanged files from DB for complete structure
|
|
53
|
+
loadUnchangedFilesFromDb(ctx);
|
|
86
54
|
}
|
|
87
55
|
|
|
88
56
|
// Build directory structure
|
|
89
57
|
const t0 = performance.now();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
buildStructure: (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
58
|
+
if (useSmallIncrementalFastPath) {
|
|
59
|
+
updateChangedFileMetrics(ctx, changedFileList!);
|
|
60
|
+
} else {
|
|
61
|
+
const relDirs = new Set<string>();
|
|
62
|
+
for (const absDir of discoveredDirs) {
|
|
63
|
+
relDirs.add(normalizePath(path.relative(rootDir, absDir)));
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const { buildStructure: buildStructureFn } = (await import(
|
|
67
|
+
'../../../../features/structure.js'
|
|
68
|
+
)) as {
|
|
69
|
+
buildStructure: (
|
|
70
|
+
db: PipelineContext['db'],
|
|
71
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
72
|
+
rootDir: string,
|
|
73
|
+
lineCountMap: Map<string, number>,
|
|
74
|
+
directories: Set<string>,
|
|
75
|
+
changedFiles: string[] | null,
|
|
76
|
+
) => void;
|
|
77
|
+
};
|
|
78
|
+
const changedFilePaths = isFullBuild ? null : [...allSymbols.keys()];
|
|
79
|
+
buildStructureFn(db, fileSymbols, rootDir, ctx.lineCountMap, relDirs, changedFilePaths);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
debug(`Structure analysis failed: ${(err as Error).message}`);
|
|
82
|
+
}
|
|
111
83
|
}
|
|
112
84
|
ctx.timing.structureMs = performance.now() - t0;
|
|
113
85
|
|
|
114
|
-
// Classify node roles
|
|
86
|
+
// Classify node roles (incremental: only reclassify changed files' nodes)
|
|
115
87
|
const t1 = performance.now();
|
|
116
88
|
try {
|
|
117
89
|
const { classifyNodeRoles } = (await import('../../../../features/structure.js')) as {
|
|
118
|
-
classifyNodeRoles: (
|
|
90
|
+
classifyNodeRoles: (
|
|
91
|
+
db: PipelineContext['db'],
|
|
92
|
+
changedFiles?: string[] | null,
|
|
93
|
+
) => Record<string, number>;
|
|
119
94
|
};
|
|
120
|
-
const roleSummary = classifyNodeRoles(db);
|
|
95
|
+
const roleSummary = classifyNodeRoles(db, changedFileList);
|
|
121
96
|
debug(
|
|
122
|
-
`Roles: ${Object.entries(
|
|
97
|
+
`Roles${changedFileList ? ` (incremental, ${changedFileList.length} files)` : ''}: ${Object.entries(
|
|
98
|
+
roleSummary,
|
|
99
|
+
)
|
|
123
100
|
.map(([r, c]) => `${r}=${c}`)
|
|
124
101
|
.join(', ')}`,
|
|
125
102
|
);
|
|
@@ -128,3 +105,155 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
128
105
|
}
|
|
129
106
|
ctx.timing.rolesMs = performance.now() - t1;
|
|
130
107
|
}
|
|
108
|
+
|
|
109
|
+
// ── Small incremental fast path ──────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* For small incremental builds, update only the changed files' node_metrics
|
|
113
|
+
* using targeted SQL queries. Skips the full DB load of all definitions
|
|
114
|
+
* (~8ms) and full structure rebuild (~15ms), replacing them with per-file
|
|
115
|
+
* indexed queries (~1-2ms total for 1-5 files).
|
|
116
|
+
*
|
|
117
|
+
* Directory metrics are not recomputed — a 1-5 file change won't
|
|
118
|
+
* meaningfully alter directory-level cohesion or symbol counts.
|
|
119
|
+
*/
|
|
120
|
+
function updateChangedFileMetrics(ctx: PipelineContext, changedFiles: string[]): void {
|
|
121
|
+
const { db } = ctx;
|
|
122
|
+
|
|
123
|
+
const getFileNodeId = db.prepare(
|
|
124
|
+
"SELECT id FROM nodes WHERE name = ? AND kind = 'file' AND file = ? AND line = 0",
|
|
125
|
+
);
|
|
126
|
+
const getSymbolCount = db.prepare(
|
|
127
|
+
"SELECT COUNT(*) as c FROM nodes WHERE file = ? AND kind != 'file' AND kind != 'directory'",
|
|
128
|
+
);
|
|
129
|
+
const getImportCount = db.prepare(`
|
|
130
|
+
SELECT COUNT(DISTINCT n2.file) AS cnt FROM edges e
|
|
131
|
+
JOIN nodes n1 ON e.source_id = n1.id
|
|
132
|
+
JOIN nodes n2 ON e.target_id = n2.id
|
|
133
|
+
WHERE e.kind = 'imports' AND n1.file = ?
|
|
134
|
+
`);
|
|
135
|
+
const getFanIn = db.prepare(`
|
|
136
|
+
SELECT COUNT(DISTINCT n_src.file) AS cnt FROM edges e
|
|
137
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
138
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
139
|
+
WHERE e.kind = 'imports' AND n_tgt.file = ? AND n_src.file != n_tgt.file
|
|
140
|
+
`);
|
|
141
|
+
const getFanOut = db.prepare(`
|
|
142
|
+
SELECT COUNT(DISTINCT n_tgt.file) AS cnt FROM edges e
|
|
143
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
144
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
145
|
+
WHERE e.kind = 'imports' AND n_src.file = ? AND n_src.file != n_tgt.file
|
|
146
|
+
`);
|
|
147
|
+
const upsertMetric = db.prepare(`
|
|
148
|
+
INSERT OR REPLACE INTO node_metrics
|
|
149
|
+
(node_id, line_count, symbol_count, import_count, export_count, fan_in, fan_out, cohesion, file_count)
|
|
150
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
151
|
+
`);
|
|
152
|
+
|
|
153
|
+
db.transaction(() => {
|
|
154
|
+
for (const relPath of changedFiles) {
|
|
155
|
+
const fileRow = getFileNodeId.get(relPath, relPath) as { id: number } | undefined;
|
|
156
|
+
if (!fileRow) continue;
|
|
157
|
+
|
|
158
|
+
const lineCount = ctx.lineCountMap.get(relPath) || 0;
|
|
159
|
+
const symbolCount = (getSymbolCount.get(relPath) as { c: number }).c;
|
|
160
|
+
const importCount = (getImportCount.get(relPath) as { cnt: number }).cnt;
|
|
161
|
+
const exportCount = ctx.fileSymbols.get(relPath)?.exports.length || 0;
|
|
162
|
+
const fanIn = (getFanIn.get(relPath) as { cnt: number }).cnt;
|
|
163
|
+
const fanOut = (getFanOut.get(relPath) as { cnt: number }).cnt;
|
|
164
|
+
|
|
165
|
+
upsertMetric.run(
|
|
166
|
+
fileRow.id,
|
|
167
|
+
lineCount,
|
|
168
|
+
symbolCount,
|
|
169
|
+
importCount,
|
|
170
|
+
exportCount,
|
|
171
|
+
fanIn,
|
|
172
|
+
fanOut,
|
|
173
|
+
null,
|
|
174
|
+
null,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
})();
|
|
178
|
+
|
|
179
|
+
debug(`Structure (fast path): updated metrics for ${changedFiles.length} files`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Full incremental DB load (medium/large changes) ──────────────────────
|
|
183
|
+
|
|
184
|
+
function loadUnchangedFilesFromDb(ctx: PipelineContext): void {
|
|
185
|
+
const { db, fileSymbols, rootDir } = ctx;
|
|
186
|
+
|
|
187
|
+
const existingFiles = db
|
|
188
|
+
.prepare("SELECT DISTINCT file FROM nodes WHERE kind = 'file'")
|
|
189
|
+
.all() as Array<{ file: string }>;
|
|
190
|
+
|
|
191
|
+
// Batch load: all definitions, import counts, and line counts in single queries
|
|
192
|
+
const allDefs = db
|
|
193
|
+
.prepare(
|
|
194
|
+
"SELECT file, name, kind, line FROM nodes WHERE kind != 'file' AND kind != 'directory'",
|
|
195
|
+
)
|
|
196
|
+
.all() as Array<{ file: string; name: string; kind: string; line: number }>;
|
|
197
|
+
const defsByFileMap = new Map<string, Array<{ name: string; kind: string; line: number }>>();
|
|
198
|
+
for (const row of allDefs) {
|
|
199
|
+
let arr = defsByFileMap.get(row.file);
|
|
200
|
+
if (!arr) {
|
|
201
|
+
arr = [];
|
|
202
|
+
defsByFileMap.set(row.file, arr);
|
|
203
|
+
}
|
|
204
|
+
arr.push({ name: row.name, kind: row.kind, line: row.line });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const allImportCounts = db
|
|
208
|
+
.prepare(
|
|
209
|
+
`SELECT n1.file, COUNT(DISTINCT n2.file) AS cnt FROM edges e
|
|
210
|
+
JOIN nodes n1 ON e.source_id = n1.id
|
|
211
|
+
JOIN nodes n2 ON e.target_id = n2.id
|
|
212
|
+
WHERE e.kind = 'imports'
|
|
213
|
+
GROUP BY n1.file`,
|
|
214
|
+
)
|
|
215
|
+
.all() as Array<{ file: string; cnt: number }>;
|
|
216
|
+
const importCountMap = new Map<string, number>();
|
|
217
|
+
for (const row of allImportCounts) {
|
|
218
|
+
importCountMap.set(row.file, row.cnt);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const cachedLineCounts = new Map<string, number>();
|
|
222
|
+
for (const row of db
|
|
223
|
+
.prepare(
|
|
224
|
+
`SELECT n.name AS file, m.line_count
|
|
225
|
+
FROM node_metrics m JOIN nodes n ON m.node_id = n.id
|
|
226
|
+
WHERE n.kind = 'file'`,
|
|
227
|
+
)
|
|
228
|
+
.all() as Array<{ file: string; line_count: number }>) {
|
|
229
|
+
cachedLineCounts.set(row.file, row.line_count);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let loadedFromDb = 0;
|
|
233
|
+
for (const { file: relPath } of existingFiles) {
|
|
234
|
+
if (!fileSymbols.has(relPath)) {
|
|
235
|
+
const importCount = importCountMap.get(relPath) || 0;
|
|
236
|
+
fileSymbols.set(relPath, {
|
|
237
|
+
definitions: defsByFileMap.get(relPath) || [],
|
|
238
|
+
imports: new Array(importCount) as unknown as ExtractorOutput['imports'],
|
|
239
|
+
exports: [],
|
|
240
|
+
} as unknown as ExtractorOutput);
|
|
241
|
+
loadedFromDb++;
|
|
242
|
+
}
|
|
243
|
+
if (!ctx.lineCountMap.has(relPath)) {
|
|
244
|
+
const cached = cachedLineCounts.get(relPath);
|
|
245
|
+
if (cached != null) {
|
|
246
|
+
ctx.lineCountMap.set(relPath, cached);
|
|
247
|
+
} else {
|
|
248
|
+
const absPath = path.join(rootDir, relPath);
|
|
249
|
+
try {
|
|
250
|
+
const content = readFileSafe(absPath);
|
|
251
|
+
ctx.lineCountMap.set(relPath, content.split('\n').length);
|
|
252
|
+
} catch {
|
|
253
|
+
ctx.lineCountMap.set(relPath, 0);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
debug(`Structure: ${fileSymbols.size} files (${loadedFromDb} loaded from DB)`);
|
|
259
|
+
}
|