@optave/codegraph 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -22
- 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/rules/javascript.d.ts.map +1 -1
- package/dist/ast-analysis/rules/javascript.js +1 -0
- 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 +0 -1
- package/dist/ast-analysis/shared.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 +103 -35
- package/dist/ast-analysis/visitors/ast-store-visitor.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/better-sqlite3.d.ts +3 -0
- package/dist/db/better-sqlite3.d.ts.map +1 -0
- package/dist/db/better-sqlite3.js +19 -0
- package/dist/db/better-sqlite3.js.map +1 -0
- package/dist/db/connection.d.ts +30 -2
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +167 -4
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- 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 +9 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/query-builder.d.ts +5 -5
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +20 -4
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/index.d.ts +1 -0
- package/dist/db/repository/index.d.ts.map +1 -1
- package/dist/db/repository/index.js +1 -0
- package/dist/db/repository/index.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +58 -0
- package/dist/db/repository/native-repository.d.ts.map +1 -0
- package/dist/db/repository/native-repository.js +261 -0
- package/dist/db/repository/native-repository.js.map +1 -0
- package/dist/db/repository/nodes.d.ts +4 -4
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +6 -6
- package/dist/db/repository/nodes.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 +3 -3
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js +1 -0
- 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 +34 -6
- 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 +113 -15
- 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 +186 -62
- package/dist/domain/graph/builder/stages/build-structure.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 +71 -7
- 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 +42 -20
- 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 +111 -64
- 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 +104 -9
- 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 +12 -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 +111 -98
- 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 +4 -3
- package/dist/extractors/rust.js.map +1 -1
- package/dist/features/ast.d.ts +14 -1
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +38 -1
- package/dist/features/ast.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 +5 -4
- 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.js +2 -2
- 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 +4 -17
- 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 +230 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +62 -11
- package/src/ast-analysis/engine.ts +3 -9
- package/src/ast-analysis/rules/javascript.ts +1 -0
- package/src/ast-analysis/shared.ts +0 -1
- package/src/ast-analysis/visitors/ast-store-visitor.ts +102 -33
- 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/better-sqlite3.ts +20 -0
- package/src/db/connection.ts +191 -16
- package/src/db/index.ts +5 -1
- package/src/db/migrations.ts +9 -0
- package/src/db/query-builder.ts +30 -5
- package/src/db/repository/index.ts +1 -0
- package/src/db/repository/native-repository.ts +361 -0
- package/src/db/repository/nodes.ts +7 -3
- 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 +4 -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 +37 -5
- package/src/domain/graph/builder/stages/build-edges.ts +131 -20
- package/src/domain/graph/builder/stages/build-structure.ts +245 -80
- package/src/domain/graph/builder/stages/collect-files.ts +84 -7
- package/src/domain/graph/builder/stages/detect-changes.ts +49 -32
- package/src/domain/graph/builder/stages/finalize.ts +132 -84
- package/src/domain/graph/builder/stages/insert-nodes.ts +141 -18
- 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 +12 -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 +112 -97
- package/src/extractors/php.ts +2 -2
- package/src/extractors/python.ts +2 -2
- package/src/extractors/rust.ts +4 -3
- package/src/features/ast.ts +66 -1
- package/src/features/audit.ts +1 -2
- package/src/features/branch-compare.ts +6 -10
- 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 +3 -3
- 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 +4 -20
- 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 +277 -10
- package/src/vendor.d.ts +0 -39
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { performance } from 'node:perf_hooks';
|
|
8
|
-
import { debug } from '
|
|
9
|
-
import { normalizePath } from '
|
|
10
|
-
import type { ExtractorOutput } from '
|
|
8
|
+
import { debug } from '#infrastructure/logger.js';
|
|
9
|
+
import { normalizePath } from '#shared/constants.js';
|
|
10
|
+
import type { ExtractorOutput } from '#types';
|
|
11
11
|
import type { PipelineContext } from '../context.js';
|
|
12
12
|
import { readFileSafe } from '../helpers.js';
|
|
13
13
|
|
|
@@ -32,94 +32,107 @@ 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
|
-
const lineCountByFile = db.prepare(
|
|
50
|
-
`SELECT n.name AS file, m.line_count
|
|
51
|
-
FROM node_metrics m JOIN nodes n ON m.node_id = n.id
|
|
52
|
-
WHERE n.kind = 'file'`,
|
|
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
|
-
}
|
|
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
|
+
? (
|
|
44
|
+
(ctx.nativeDb
|
|
45
|
+
? ctx.nativeDb.queryGet("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'", [])
|
|
46
|
+
: db.prepare("SELECT COUNT(*) as c FROM nodes WHERE kind = 'file'").get()) as {
|
|
47
|
+
c: number;
|
|
82
48
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
49
|
+
).c
|
|
50
|
+
: 0;
|
|
51
|
+
const useSmallIncrementalFastPath =
|
|
52
|
+
!isFullBuild &&
|
|
53
|
+
changedFileList != null &&
|
|
54
|
+
changedFileList.length <= 5 &&
|
|
55
|
+
existingFileCount > 20;
|
|
56
|
+
|
|
57
|
+
if (!isFullBuild && !useSmallIncrementalFastPath) {
|
|
58
|
+
// Medium/large incremental: load unchanged files from DB for complete structure
|
|
59
|
+
loadUnchangedFilesFromDb(ctx);
|
|
86
60
|
}
|
|
87
61
|
|
|
88
62
|
// Build directory structure
|
|
89
63
|
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
|
-
|
|
64
|
+
if (useSmallIncrementalFastPath) {
|
|
65
|
+
updateChangedFileMetrics(ctx, changedFileList!);
|
|
66
|
+
} else {
|
|
67
|
+
const relDirs = new Set<string>();
|
|
68
|
+
for (const absDir of discoveredDirs) {
|
|
69
|
+
relDirs.add(normalizePath(path.relative(rootDir, absDir)));
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const { buildStructure: buildStructureFn } = (await import(
|
|
73
|
+
'../../../../features/structure.js'
|
|
74
|
+
)) as {
|
|
75
|
+
buildStructure: (
|
|
76
|
+
db: PipelineContext['db'],
|
|
77
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
78
|
+
rootDir: string,
|
|
79
|
+
lineCountMap: Map<string, number>,
|
|
80
|
+
directories: Set<string>,
|
|
81
|
+
changedFiles: string[] | null,
|
|
82
|
+
) => void;
|
|
83
|
+
};
|
|
84
|
+
const changedFilePaths = isFullBuild ? null : [...allSymbols.keys()];
|
|
85
|
+
buildStructureFn(db, fileSymbols, rootDir, ctx.lineCountMap, relDirs, changedFilePaths);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
debug(`Structure analysis failed: ${(err as Error).message}`);
|
|
88
|
+
}
|
|
111
89
|
}
|
|
112
90
|
ctx.timing.structureMs = performance.now() - t0;
|
|
113
91
|
|
|
114
|
-
// Classify node roles
|
|
92
|
+
// Classify node roles (incremental: only reclassify changed files' nodes)
|
|
115
93
|
const t1 = performance.now();
|
|
116
94
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
95
|
+
let roleSummary: Record<string, number> | null = null;
|
|
96
|
+
|
|
97
|
+
// Use NativeDatabase persistent connection (Phase 6.15+).
|
|
98
|
+
// Standalone napi functions were removed in 6.17 — falls through to JS if nativeDb unavailable.
|
|
99
|
+
if (ctx.nativeDb?.classifyRolesFull) {
|
|
100
|
+
const nativeResult =
|
|
101
|
+
changedFileList && changedFileList.length > 0
|
|
102
|
+
? ctx.nativeDb.classifyRolesIncremental(changedFileList)
|
|
103
|
+
: ctx.nativeDb.classifyRolesFull();
|
|
104
|
+
if (nativeResult) {
|
|
105
|
+
roleSummary = {
|
|
106
|
+
entry: nativeResult.entry,
|
|
107
|
+
core: nativeResult.core,
|
|
108
|
+
utility: nativeResult.utility,
|
|
109
|
+
adapter: nativeResult.adapter,
|
|
110
|
+
dead: nativeResult.dead,
|
|
111
|
+
'dead-leaf': nativeResult.deadLeaf,
|
|
112
|
+
'dead-entry': nativeResult.deadEntry,
|
|
113
|
+
'dead-ffi': nativeResult.deadFfi,
|
|
114
|
+
'dead-unresolved': nativeResult.deadUnresolved,
|
|
115
|
+
'test-only': nativeResult.testOnly,
|
|
116
|
+
leaf: nativeResult.leaf,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Fall back to JS path
|
|
122
|
+
if (!roleSummary) {
|
|
123
|
+
const { classifyNodeRoles } = (await import('../../../../features/structure.js')) as {
|
|
124
|
+
classifyNodeRoles: (
|
|
125
|
+
db: PipelineContext['db'],
|
|
126
|
+
changedFiles?: string[] | null,
|
|
127
|
+
) => Record<string, number>;
|
|
128
|
+
};
|
|
129
|
+
roleSummary = classifyNodeRoles(db, changedFileList);
|
|
130
|
+
}
|
|
131
|
+
|
|
121
132
|
debug(
|
|
122
|
-
`Roles: ${Object.entries(
|
|
133
|
+
`Roles${changedFileList ? ` (incremental, ${changedFileList.length} files)` : ''}: ${Object.entries(
|
|
134
|
+
roleSummary,
|
|
135
|
+
)
|
|
123
136
|
.map(([r, c]) => `${r}=${c}`)
|
|
124
137
|
.join(', ')}`,
|
|
125
138
|
);
|
|
@@ -128,3 +141,155 @@ export async function buildStructure(ctx: PipelineContext): Promise<void> {
|
|
|
128
141
|
}
|
|
129
142
|
ctx.timing.rolesMs = performance.now() - t1;
|
|
130
143
|
}
|
|
144
|
+
|
|
145
|
+
// ── Small incremental fast path ──────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* For small incremental builds, update only the changed files' node_metrics
|
|
149
|
+
* using targeted SQL queries. Skips the full DB load of all definitions
|
|
150
|
+
* (~8ms) and full structure rebuild (~15ms), replacing them with per-file
|
|
151
|
+
* indexed queries (~1-2ms total for 1-5 files).
|
|
152
|
+
*
|
|
153
|
+
* Directory metrics are not recomputed — a 1-5 file change won't
|
|
154
|
+
* meaningfully alter directory-level cohesion or symbol counts.
|
|
155
|
+
*/
|
|
156
|
+
function updateChangedFileMetrics(ctx: PipelineContext, changedFiles: string[]): void {
|
|
157
|
+
const { db } = ctx;
|
|
158
|
+
|
|
159
|
+
const getFileNodeId = db.prepare(
|
|
160
|
+
"SELECT id FROM nodes WHERE name = ? AND kind = 'file' AND file = ? AND line = 0",
|
|
161
|
+
);
|
|
162
|
+
const getSymbolCount = db.prepare(
|
|
163
|
+
"SELECT COUNT(*) as c FROM nodes WHERE file = ? AND kind != 'file' AND kind != 'directory'",
|
|
164
|
+
);
|
|
165
|
+
const getImportCount = db.prepare(`
|
|
166
|
+
SELECT COUNT(DISTINCT n2.file) AS cnt FROM edges e
|
|
167
|
+
JOIN nodes n1 ON e.source_id = n1.id
|
|
168
|
+
JOIN nodes n2 ON e.target_id = n2.id
|
|
169
|
+
WHERE e.kind = 'imports' AND n1.file = ?
|
|
170
|
+
`);
|
|
171
|
+
const getFanIn = db.prepare(`
|
|
172
|
+
SELECT COUNT(DISTINCT n_src.file) AS cnt FROM edges e
|
|
173
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
174
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
175
|
+
WHERE e.kind = 'imports' AND n_tgt.file = ? AND n_src.file != n_tgt.file
|
|
176
|
+
`);
|
|
177
|
+
const getFanOut = db.prepare(`
|
|
178
|
+
SELECT COUNT(DISTINCT n_tgt.file) AS cnt FROM edges e
|
|
179
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
180
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
181
|
+
WHERE e.kind = 'imports' AND n_src.file = ? AND n_src.file != n_tgt.file
|
|
182
|
+
`);
|
|
183
|
+
const upsertMetric = db.prepare(`
|
|
184
|
+
INSERT OR REPLACE INTO node_metrics
|
|
185
|
+
(node_id, line_count, symbol_count, import_count, export_count, fan_in, fan_out, cohesion, file_count)
|
|
186
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
187
|
+
`);
|
|
188
|
+
|
|
189
|
+
db.transaction(() => {
|
|
190
|
+
for (const relPath of changedFiles) {
|
|
191
|
+
const fileRow = getFileNodeId.get(relPath, relPath) as { id: number } | undefined;
|
|
192
|
+
if (!fileRow) continue;
|
|
193
|
+
|
|
194
|
+
const lineCount = ctx.lineCountMap.get(relPath) || 0;
|
|
195
|
+
const symbolCount = (getSymbolCount.get(relPath) as { c: number }).c;
|
|
196
|
+
const importCount = (getImportCount.get(relPath) as { cnt: number }).cnt;
|
|
197
|
+
const exportCount = ctx.fileSymbols.get(relPath)?.exports.length || 0;
|
|
198
|
+
const fanIn = (getFanIn.get(relPath) as { cnt: number }).cnt;
|
|
199
|
+
const fanOut = (getFanOut.get(relPath) as { cnt: number }).cnt;
|
|
200
|
+
|
|
201
|
+
upsertMetric.run(
|
|
202
|
+
fileRow.id,
|
|
203
|
+
lineCount,
|
|
204
|
+
symbolCount,
|
|
205
|
+
importCount,
|
|
206
|
+
exportCount,
|
|
207
|
+
fanIn,
|
|
208
|
+
fanOut,
|
|
209
|
+
null,
|
|
210
|
+
null,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
})();
|
|
214
|
+
|
|
215
|
+
debug(`Structure (fast path): updated metrics for ${changedFiles.length} files`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── Full incremental DB load (medium/large changes) ──────────────────────
|
|
219
|
+
|
|
220
|
+
function loadUnchangedFilesFromDb(ctx: PipelineContext): void {
|
|
221
|
+
const { db, fileSymbols, rootDir } = ctx;
|
|
222
|
+
|
|
223
|
+
const existingFiles = db
|
|
224
|
+
.prepare("SELECT DISTINCT file FROM nodes WHERE kind = 'file'")
|
|
225
|
+
.all() as Array<{ file: string }>;
|
|
226
|
+
|
|
227
|
+
// Batch load: all definitions, import counts, and line counts in single queries
|
|
228
|
+
const allDefs = db
|
|
229
|
+
.prepare(
|
|
230
|
+
"SELECT file, name, kind, line FROM nodes WHERE kind != 'file' AND kind != 'directory'",
|
|
231
|
+
)
|
|
232
|
+
.all() as Array<{ file: string; name: string; kind: string; line: number }>;
|
|
233
|
+
const defsByFileMap = new Map<string, Array<{ name: string; kind: string; line: number }>>();
|
|
234
|
+
for (const row of allDefs) {
|
|
235
|
+
let arr = defsByFileMap.get(row.file);
|
|
236
|
+
if (!arr) {
|
|
237
|
+
arr = [];
|
|
238
|
+
defsByFileMap.set(row.file, arr);
|
|
239
|
+
}
|
|
240
|
+
arr.push({ name: row.name, kind: row.kind, line: row.line });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const allImportCounts = db
|
|
244
|
+
.prepare(
|
|
245
|
+
`SELECT n1.file, COUNT(DISTINCT n2.file) AS cnt FROM edges e
|
|
246
|
+
JOIN nodes n1 ON e.source_id = n1.id
|
|
247
|
+
JOIN nodes n2 ON e.target_id = n2.id
|
|
248
|
+
WHERE e.kind = 'imports'
|
|
249
|
+
GROUP BY n1.file`,
|
|
250
|
+
)
|
|
251
|
+
.all() as Array<{ file: string; cnt: number }>;
|
|
252
|
+
const importCountMap = new Map<string, number>();
|
|
253
|
+
for (const row of allImportCounts) {
|
|
254
|
+
importCountMap.set(row.file, row.cnt);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const cachedLineCounts = new Map<string, number>();
|
|
258
|
+
for (const row of db
|
|
259
|
+
.prepare(
|
|
260
|
+
`SELECT n.name AS file, m.line_count
|
|
261
|
+
FROM node_metrics m JOIN nodes n ON m.node_id = n.id
|
|
262
|
+
WHERE n.kind = 'file'`,
|
|
263
|
+
)
|
|
264
|
+
.all() as Array<{ file: string; line_count: number }>) {
|
|
265
|
+
cachedLineCounts.set(row.file, row.line_count);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let loadedFromDb = 0;
|
|
269
|
+
for (const { file: relPath } of existingFiles) {
|
|
270
|
+
if (!fileSymbols.has(relPath)) {
|
|
271
|
+
const importCount = importCountMap.get(relPath) || 0;
|
|
272
|
+
fileSymbols.set(relPath, {
|
|
273
|
+
definitions: defsByFileMap.get(relPath) || [],
|
|
274
|
+
imports: new Array(importCount) as unknown as ExtractorOutput['imports'],
|
|
275
|
+
exports: [],
|
|
276
|
+
} as unknown as ExtractorOutput);
|
|
277
|
+
loadedFromDb++;
|
|
278
|
+
}
|
|
279
|
+
if (!ctx.lineCountMap.has(relPath)) {
|
|
280
|
+
const cached = cachedLineCounts.get(relPath);
|
|
281
|
+
if (cached != null) {
|
|
282
|
+
ctx.lineCountMap.set(relPath, cached);
|
|
283
|
+
} else {
|
|
284
|
+
const absPath = path.join(rootDir, relPath);
|
|
285
|
+
try {
|
|
286
|
+
const content = readFileSafe(absPath);
|
|
287
|
+
ctx.lineCountMap.set(relPath, content.split('\n').length);
|
|
288
|
+
} catch {
|
|
289
|
+
ctx.lineCountMap.set(relPath, 0);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
debug(`Structure: ${fileSymbols.size} files (${loadedFromDb} loaded from DB)`);
|
|
295
|
+
}
|
|
@@ -2,14 +2,78 @@
|
|
|
2
2
|
* Stage: collectFiles
|
|
3
3
|
*
|
|
4
4
|
* Collects all source files to process. Handles both normal and scoped rebuilds.
|
|
5
|
+
* For incremental builds with a valid journal, reconstructs the file list from
|
|
6
|
+
* the DB's file_hashes table + journal deltas, skipping the filesystem scan.
|
|
5
7
|
*/
|
|
6
8
|
import fs from 'node:fs';
|
|
7
9
|
import path from 'node:path';
|
|
8
|
-
import { info } from '
|
|
9
|
-
import { normalizePath } from '
|
|
10
|
+
import { debug, info } from '#infrastructure/logger.js';
|
|
11
|
+
import { normalizePath } from '#shared/constants.js';
|
|
12
|
+
import { readJournal } from '../../journal.js';
|
|
10
13
|
import type { PipelineContext } from '../context.js';
|
|
11
14
|
import { collectFiles as collectFilesUtil } from '../helpers.js';
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Reconstruct allFiles from DB file_hashes + journal deltas.
|
|
18
|
+
* Returns null when the fast path isn't applicable (first build, no journal, etc).
|
|
19
|
+
*/
|
|
20
|
+
function tryFastCollect(
|
|
21
|
+
ctx: PipelineContext,
|
|
22
|
+
): { files: string[]; directories: Set<string> } | null {
|
|
23
|
+
const { db, rootDir } = ctx;
|
|
24
|
+
|
|
25
|
+
// 1. Check that file_hashes table exists and has entries
|
|
26
|
+
let dbFileCount: number;
|
|
27
|
+
try {
|
|
28
|
+
dbFileCount = (db.prepare('SELECT COUNT(*) as c FROM file_hashes').get() as { c: number }).c;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (dbFileCount === 0) return null;
|
|
33
|
+
|
|
34
|
+
// 2. Read the journal — only use fast path when journal has entries,
|
|
35
|
+
// proving the watcher was active and tracking changes. An empty-but-valid
|
|
36
|
+
// journal (no watcher) could miss file deletions.
|
|
37
|
+
const journal = readJournal(rootDir);
|
|
38
|
+
if (!journal.valid) return null;
|
|
39
|
+
const hasEntries =
|
|
40
|
+
(journal.changed && journal.changed.length > 0) ||
|
|
41
|
+
(journal.removed && journal.removed.length > 0);
|
|
42
|
+
if (!hasEntries) return null;
|
|
43
|
+
|
|
44
|
+
// 3. Load existing file list from file_hashes (relative paths)
|
|
45
|
+
const dbFiles = (db.prepare('SELECT file FROM file_hashes').all() as Array<{ file: string }>).map(
|
|
46
|
+
(r) => r.file,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// 4. Apply journal deltas: remove deleted files, add new/changed files
|
|
50
|
+
const fileSet = new Set(dbFiles);
|
|
51
|
+
if (journal.removed) {
|
|
52
|
+
for (const removed of journal.removed) {
|
|
53
|
+
fileSet.delete(removed);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (journal.changed) {
|
|
57
|
+
for (const changed of journal.changed) {
|
|
58
|
+
fileSet.add(changed);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 5. Convert to absolute paths and compute directories
|
|
63
|
+
const files: string[] = [];
|
|
64
|
+
const directories = new Set<string>();
|
|
65
|
+
for (const relPath of fileSet) {
|
|
66
|
+
const absPath = path.join(rootDir, relPath);
|
|
67
|
+
files.push(absPath);
|
|
68
|
+
directories.add(path.dirname(absPath));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
debug(
|
|
72
|
+
`collectFiles fast path: ${dbFiles.length} from DB, journal: +${journal.changed?.length ?? 0}/-${journal.removed?.length ?? 0} → ${files.length} files`,
|
|
73
|
+
);
|
|
74
|
+
return { files, directories };
|
|
75
|
+
}
|
|
76
|
+
|
|
13
77
|
export async function collectFiles(ctx: PipelineContext): Promise<void> {
|
|
14
78
|
const { rootDir, config, opts } = ctx;
|
|
15
79
|
|
|
@@ -33,10 +97,23 @@ export async function collectFiles(ctx: PipelineContext): Promise<void> {
|
|
|
33
97
|
ctx.removed = missing;
|
|
34
98
|
ctx.isFullBuild = false;
|
|
35
99
|
info(`Scoped rebuild: ${existing.length} files to rebuild, ${missing.length} to purge`);
|
|
36
|
-
|
|
37
|
-
const collected = collectFilesUtil(rootDir, [], config, new Set<string>());
|
|
38
|
-
ctx.allFiles = collected.files;
|
|
39
|
-
ctx.discoveredDirs = collected.directories;
|
|
40
|
-
info(`Found ${ctx.allFiles.length} files to parse`);
|
|
100
|
+
return;
|
|
41
101
|
}
|
|
102
|
+
|
|
103
|
+
// Incremental fast path: reconstruct file list from DB + journal deltas
|
|
104
|
+
// instead of full recursive filesystem scan (~8ms savings on 473 files).
|
|
105
|
+
if (ctx.incremental && !ctx.forceFullRebuild) {
|
|
106
|
+
const fast = tryFastCollect(ctx);
|
|
107
|
+
if (fast) {
|
|
108
|
+
ctx.allFiles = fast.files;
|
|
109
|
+
ctx.discoveredDirs = fast.directories;
|
|
110
|
+
info(`Found ${ctx.allFiles.length} files (cached)`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const collected = collectFilesUtil(rootDir, [], config, new Set<string>());
|
|
116
|
+
ctx.allFiles = collected.files;
|
|
117
|
+
ctx.discoveredDirs = collected.directories;
|
|
118
|
+
info(`Found ${ctx.allFiles.length} files to parse`);
|
|
42
119
|
}
|
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
11
10
|
import { closeDb } from '../../../../db/index.js';
|
|
12
11
|
import { debug, info } from '../../../../infrastructure/logger.js';
|
|
13
12
|
import { normalizePath } from '../../../../shared/constants.js';
|
|
14
|
-
import type {
|
|
13
|
+
import type { BetterSqlite3Database, ExtractorOutput, NativeDatabase } from '../../../../types.js';
|
|
15
14
|
import { parseFilesAuto } from '../../../parser.js';
|
|
16
15
|
import { readJournal, writeJournalHeader } from '../../journal.js';
|
|
17
16
|
import type { PipelineContext } from '../context.js';
|
|
@@ -56,13 +55,19 @@ interface NeedsHashItem {
|
|
|
56
55
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
57
56
|
|
|
58
57
|
function getChangedFiles(
|
|
59
|
-
db:
|
|
58
|
+
db: BetterSqlite3Database,
|
|
60
59
|
allFiles: string[],
|
|
61
60
|
rootDir: string,
|
|
61
|
+
nativeDb?: NativeDatabase,
|
|
62
62
|
): ChangeResult {
|
|
63
63
|
let hasTable = false;
|
|
64
64
|
try {
|
|
65
|
-
|
|
65
|
+
if (nativeDb) {
|
|
66
|
+
nativeDb.queryGet('SELECT 1 FROM file_hashes LIMIT 1', []);
|
|
67
|
+
} else {
|
|
68
|
+
db.prepare('SELECT 1 FROM file_hashes LIMIT 1').get();
|
|
69
|
+
}
|
|
70
|
+
// Query succeeded → table exists (result may be undefined if table is empty)
|
|
66
71
|
hasTable = true;
|
|
67
72
|
} catch {
|
|
68
73
|
/* table doesn't exist */
|
|
@@ -76,11 +81,11 @@ function getChangedFiles(
|
|
|
76
81
|
};
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
)
|
|
83
|
-
);
|
|
84
|
+
const sql = 'SELECT file, hash, mtime, size FROM file_hashes';
|
|
85
|
+
const rows = nativeDb
|
|
86
|
+
? (nativeDb.queryAll(sql, []) as unknown as FileHashRow[])
|
|
87
|
+
: (db.prepare(sql).all() as FileHashRow[]);
|
|
88
|
+
const existing = new Map<string, FileHashRow>(rows.map((r) => [r.file, r]));
|
|
84
89
|
|
|
85
90
|
const removed = detectRemovedFiles(existing, allFiles, rootDir);
|
|
86
91
|
const journalResult = tryJournalTier(db, existing, rootDir, removed);
|
|
@@ -107,7 +112,7 @@ function detectRemovedFiles(
|
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
function tryJournalTier(
|
|
110
|
-
db:
|
|
115
|
+
db: BetterSqlite3Database,
|
|
111
116
|
existing: Map<string, FileHashRow>,
|
|
112
117
|
rootDir: string,
|
|
113
118
|
removed: string[],
|
|
@@ -227,7 +232,7 @@ function mtimeAndHashTiers(
|
|
|
227
232
|
async function runPendingAnalysis(ctx: PipelineContext): Promise<boolean> {
|
|
228
233
|
const { db, opts, engineOpts, allFiles, rootDir } = ctx;
|
|
229
234
|
const needsCfg =
|
|
230
|
-
(opts as Record<string, unknown>)
|
|
235
|
+
(opts as Record<string, unknown>).cfg !== false &&
|
|
231
236
|
(() => {
|
|
232
237
|
try {
|
|
233
238
|
return (
|
|
@@ -239,7 +244,7 @@ async function runPendingAnalysis(ctx: PipelineContext): Promise<boolean> {
|
|
|
239
244
|
}
|
|
240
245
|
})();
|
|
241
246
|
const needsDataflow =
|
|
242
|
-
(opts as Record<string, unknown>)
|
|
247
|
+
(opts as Record<string, unknown>).dataflow !== false &&
|
|
243
248
|
(() => {
|
|
244
249
|
try {
|
|
245
250
|
return (
|
|
@@ -255,7 +260,7 @@ async function runPendingAnalysis(ctx: PipelineContext): Promise<boolean> {
|
|
|
255
260
|
info('No file changes. Running pending analysis pass...');
|
|
256
261
|
const analysisOpts = {
|
|
257
262
|
...engineOpts,
|
|
258
|
-
dataflow: needsDataflow && (opts as Record<string, unknown>)
|
|
263
|
+
dataflow: needsDataflow && (opts as Record<string, unknown>).dataflow !== false,
|
|
259
264
|
};
|
|
260
265
|
const analysisSymbols: Map<string, ExtractorOutput> = await parseFilesAuto(
|
|
261
266
|
allFiles,
|
|
@@ -295,7 +300,7 @@ function healMetadata(ctx: PipelineContext): void {
|
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
function findReverseDependencies(
|
|
298
|
-
db:
|
|
303
|
+
db: BetterSqlite3Database,
|
|
299
304
|
changedRelPaths: Set<string>,
|
|
300
305
|
rootDir: string,
|
|
301
306
|
): Set<string> {
|
|
@@ -326,24 +331,36 @@ function purgeAndAddReverseDeps(
|
|
|
326
331
|
reverseDeps: Set<string>,
|
|
327
332
|
): void {
|
|
328
333
|
const { db, rootDir } = ctx;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
334
|
+
const hasPurge = changePaths.length > 0 || ctx.removed.length > 0;
|
|
335
|
+
const hasReverseDeps = reverseDeps.size > 0;
|
|
336
|
+
const reverseDepList = hasReverseDeps ? [...reverseDeps] : [];
|
|
337
|
+
|
|
338
|
+
if (hasPurge || hasReverseDeps) {
|
|
339
|
+
const filesToPurge = hasPurge ? [...ctx.removed, ...changePaths] : [];
|
|
340
|
+
// Prefer NativeDatabase: purge + reverse-dep edge deletion in one transaction (#670)
|
|
341
|
+
if (ctx.nativeDb?.purgeFilesData) {
|
|
342
|
+
ctx.nativeDb.purgeFilesData(filesToPurge, false, hasReverseDeps ? reverseDepList : undefined);
|
|
343
|
+
} else {
|
|
344
|
+
if (hasPurge) {
|
|
345
|
+
purgeFilesFromGraph(db, filesToPurge, { purgeHashes: false });
|
|
346
|
+
}
|
|
347
|
+
if (hasReverseDeps) {
|
|
348
|
+
const deleteOutgoingEdgesForFile = db.prepare(
|
|
349
|
+
'DELETE FROM edges WHERE source_id IN (SELECT id FROM nodes WHERE file = ?)',
|
|
350
|
+
);
|
|
351
|
+
for (const relPath of reverseDepList) {
|
|
352
|
+
deleteOutgoingEdgesForFile.run(relPath);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
342
355
|
}
|
|
343
356
|
}
|
|
357
|
+
for (const relPath of reverseDeps) {
|
|
358
|
+
const absPath = path.join(rootDir, relPath);
|
|
359
|
+
ctx.parseChanges.push({ file: absPath, relPath, _reverseDepOnly: true });
|
|
360
|
+
}
|
|
344
361
|
}
|
|
345
362
|
|
|
346
|
-
function detectHasEmbeddings(db:
|
|
363
|
+
function detectHasEmbeddings(db: BetterSqlite3Database): boolean {
|
|
347
364
|
try {
|
|
348
365
|
db.prepare('SELECT 1 FROM embeddings LIMIT 1').get();
|
|
349
366
|
return true;
|
|
@@ -359,7 +376,7 @@ function handleScopedBuild(ctx: PipelineContext): void {
|
|
|
359
376
|
(item) => item.relPath || normalizePath(path.relative(rootDir, item.file)),
|
|
360
377
|
);
|
|
361
378
|
let reverseDeps = new Set<string>();
|
|
362
|
-
if (!(opts as Record<string, unknown>)
|
|
379
|
+
if (!(opts as Record<string, unknown>).noReverseDeps) {
|
|
363
380
|
const changedRelPaths = new Set<string>([...changePaths, ...ctx.removed]);
|
|
364
381
|
reverseDeps = findReverseDependencies(db, changedRelPaths, rootDir);
|
|
365
382
|
}
|
|
@@ -386,7 +403,7 @@ function handleIncrementalBuild(ctx: PipelineContext): void {
|
|
|
386
403
|
const { db, rootDir, opts } = ctx;
|
|
387
404
|
ctx.hasEmbeddings = detectHasEmbeddings(db);
|
|
388
405
|
let reverseDeps = new Set<string>();
|
|
389
|
-
if (!(opts as Record<string, unknown>)
|
|
406
|
+
if (!(opts as Record<string, unknown>).noReverseDeps) {
|
|
390
407
|
const changedRelPaths = new Set<string>();
|
|
391
408
|
for (const item of ctx.parseChanges) {
|
|
392
409
|
changedRelPaths.add(item.relPath || normalizePath(path.relative(rootDir, item.file)));
|
|
@@ -410,13 +427,13 @@ function handleIncrementalBuild(ctx: PipelineContext): void {
|
|
|
410
427
|
|
|
411
428
|
export async function detectChanges(ctx: PipelineContext): Promise<void> {
|
|
412
429
|
const { db, allFiles, rootDir, incremental, forceFullRebuild, opts } = ctx;
|
|
413
|
-
if ((opts as Record<string, unknown>)
|
|
430
|
+
if ((opts as Record<string, unknown>).scope) {
|
|
414
431
|
handleScopedBuild(ctx);
|
|
415
432
|
return;
|
|
416
433
|
}
|
|
417
434
|
const increResult =
|
|
418
435
|
incremental && !forceFullRebuild
|
|
419
|
-
? getChangedFiles(db, allFiles, rootDir)
|
|
436
|
+
? getChangedFiles(db, allFiles, rootDir, ctx.nativeDb)
|
|
420
437
|
: {
|
|
421
438
|
changed: allFiles.map((f): ChangedFile => ({ file: f })),
|
|
422
439
|
removed: [] as string[],
|