@optave/codegraph 3.13.0 → 3.15.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 +35 -34
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +38 -40
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/b2.d.ts +7 -0
- package/dist/ast-analysis/rules/b2.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b2.js +240 -0
- package/dist/ast-analysis/rules/b2.js.map +1 -0
- package/dist/ast-analysis/rules/b3.d.ts +6 -0
- package/dist/ast-analysis/rules/b3.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b3.js +105 -0
- package/dist/ast-analysis/rules/b3.js.map +1 -0
- package/dist/ast-analysis/rules/b4.d.ts +9 -0
- package/dist/ast-analysis/rules/b4.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b4.js +361 -0
- package/dist/ast-analysis/rules/b4.js.map +1 -0
- package/dist/ast-analysis/rules/b5.d.ts +4 -0
- package/dist/ast-analysis/rules/b5.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b5.js +52 -0
- package/dist/ast-analysis/rules/b5.js.map +1 -0
- package/dist/ast-analysis/rules/c.d.ts +4 -0
- package/dist/ast-analysis/rules/c.d.ts.map +1 -0
- package/dist/ast-analysis/rules/c.js +143 -0
- package/dist/ast-analysis/rules/c.js.map +1 -0
- package/dist/ast-analysis/rules/index.d.ts.map +1 -1
- package/dist/ast-analysis/rules/index.js +34 -0
- package/dist/ast-analysis/rules/index.js.map +1 -1
- package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
- package/dist/ast-analysis/rules/javascript.js +3 -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 +2 -0
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +1 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +5 -0
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +60 -47
- package/dist/ast-analysis/visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/cfg-visitor.js +126 -76
- 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 +27 -15
- 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 +54 -21
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +137 -134
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/roles.d.ts.map +1 -1
- package/dist/cli/commands/roles.js +6 -1
- package/dist/cli/commands/roles.js.map +1 -1
- package/dist/db/better-sqlite3.d.ts +2 -1
- package/dist/db/better-sqlite3.d.ts.map +1 -1
- package/dist/db/better-sqlite3.js.map +1 -1
- package/dist/db/connection.d.ts +7 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +20 -5
- 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 +68 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/repository/build-stmts.d.ts.map +1 -1
- package/dist/db/repository/build-stmts.js +18 -0
- package/dist/db/repository/build-stmts.js.map +1 -1
- package/dist/db/repository/dataflow.d.ts +5 -0
- package/dist/db/repository/dataflow.d.ts.map +1 -1
- package/dist/db/repository/dataflow.js +14 -0
- package/dist/db/repository/dataflow.js.map +1 -1
- package/dist/db/repository/index.d.ts +1 -1
- package/dist/db/repository/index.d.ts.map +1 -1
- package/dist/db/repository/index.js +1 -1
- package/dist/db/repository/index.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +47 -34
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/domain/analysis/context.d.ts +2 -2
- package/dist/domain/analysis/dependencies.d.ts +2 -2
- package/dist/domain/analysis/diff-impact.d.ts +2 -2
- package/dist/domain/analysis/fn-impact.d.ts +3 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +4 -0
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts +2 -2
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +32 -5
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/roles.d.ts +7 -1
- package/dist/domain/analysis/roles.d.ts.map +1 -1
- package/dist/domain/analysis/roles.js +16 -0
- package/dist/domain/analysis/roles.js.map +1 -1
- package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
- package/dist/domain/graph/builder/call-resolver.d.ts +17 -5
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
- package/dist/domain/graph/builder/call-resolver.js +85 -220
- package/dist/domain/graph/builder/call-resolver.js.map +1 -1
- package/dist/domain/graph/builder/context.d.ts +1 -0
- 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 +16 -1
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +162 -72
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +166 -97
- 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 +10 -4
- 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 +496 -250
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +10 -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 +2 -1
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.js +895 -545
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
- package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
- package/dist/domain/graph/resolver/points-to.js +105 -57
- package/dist/domain/graph/resolver/points-to.js.map +1 -1
- package/dist/domain/graph/resolver/strategy.d.ts +61 -0
- package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
- package/dist/domain/graph/resolver/strategy.js +222 -0
- package/dist/domain/graph/resolver/strategy.js.map +1 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +16 -9
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -0
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +12 -2
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/queries.d.ts +1 -1
- package/dist/domain/queries.d.ts.map +1 -1
- package/dist/domain/queries.js +1 -1
- package/dist/domain/queries.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +3 -0
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
- package/dist/domain/wasm-worker-pool.js +24 -5
- package/dist/domain/wasm-worker-pool.js.map +1 -1
- package/dist/domain/wasm-worker-protocol.d.ts +7 -0
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
- package/dist/extractors/dart.js +48 -3
- package/dist/extractors/dart.js.map +1 -1
- package/dist/extractors/groovy.js +62 -3
- package/dist/extractors/groovy.js.map +1 -1
- package/dist/extractors/helpers.d.ts +4 -2
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +5 -1
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +77 -1
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +549 -163
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +58 -3
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.js +25 -2
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/scala.js +62 -2
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/swift.js +52 -3
- package/dist/extractors/swift.js.map +1 -1
- package/dist/features/audit.js +26 -23
- package/dist/features/audit.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +12 -9
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +25 -18
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +18 -5
- package/dist/features/check.js.map +1 -1
- package/dist/features/communities.d.ts +4 -2
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +6 -4
- package/dist/features/communities.js.map +1 -1
- package/dist/features/dataflow.d.ts +60 -0
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +530 -6
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/manifesto.d.ts.map +1 -1
- package/dist/features/manifesto.js +59 -72
- package/dist/features/manifesto.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +27 -22
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/snapshot.d.ts.map +1 -1
- package/dist/features/snapshot.js +36 -28
- package/dist/features/snapshot.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +150 -62
- package/dist/features/structure.js.map +1 -1
- package/dist/features/triage.d.ts.map +1 -1
- package/dist/features/triage.js +18 -11
- package/dist/features/triage.js.map +1 -1
- package/dist/graph/algorithms/bfs.d.ts +1 -1
- package/dist/graph/algorithms/bfs.d.ts.map +1 -1
- package/dist/graph/algorithms/bfs.js +14 -13
- package/dist/graph/algorithms/bfs.js.map +1 -1
- package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
- package/dist/graph/algorithms/tarjan.js +5 -0
- package/dist/graph/algorithms/tarjan.js.map +1 -1
- package/dist/graph/builders/dependency.js +28 -22
- package/dist/graph/builders/dependency.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts +10 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +60 -6
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +10 -0
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +31 -3
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +0 -7
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +29 -13
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/infrastructure/update-check.d.ts.map +1 -1
- package/dist/infrastructure/update-check.js +49 -31
- package/dist/infrastructure/update-check.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -10
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/ast-query.d.ts +1 -1
- package/dist/mcp/tools/ast-query.d.ts.map +1 -1
- package/dist/mcp/tools/audit.d.ts +1 -1
- package/dist/mcp/tools/audit.d.ts.map +1 -1
- package/dist/mcp/tools/batch-query.d.ts +1 -1
- package/dist/mcp/tools/batch-query.d.ts.map +1 -1
- package/dist/mcp/tools/branch-compare.d.ts +1 -1
- package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
- package/dist/mcp/tools/brief.d.ts +1 -1
- package/dist/mcp/tools/brief.d.ts.map +1 -1
- package/dist/mcp/tools/cfg.d.ts +1 -1
- package/dist/mcp/tools/cfg.d.ts.map +1 -1
- package/dist/mcp/tools/check.d.ts +1 -1
- package/dist/mcp/tools/check.d.ts.map +1 -1
- package/dist/mcp/tools/co-changes.d.ts +1 -1
- package/dist/mcp/tools/co-changes.d.ts.map +1 -1
- package/dist/mcp/tools/code-owners.d.ts +1 -1
- package/dist/mcp/tools/code-owners.d.ts.map +1 -1
- package/dist/mcp/tools/communities.d.ts +1 -1
- package/dist/mcp/tools/communities.d.ts.map +1 -1
- package/dist/mcp/tools/complexity.d.ts +1 -1
- package/dist/mcp/tools/complexity.d.ts.map +1 -1
- package/dist/mcp/tools/context.d.ts +1 -1
- package/dist/mcp/tools/context.d.ts.map +1 -1
- package/dist/mcp/tools/dataflow.d.ts +1 -1
- package/dist/mcp/tools/dataflow.d.ts.map +1 -1
- package/dist/mcp/tools/diff-impact.d.ts +1 -1
- package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
- package/dist/mcp/tools/execution-flow.d.ts +1 -1
- package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
- package/dist/mcp/tools/export-graph.d.ts +1 -1
- package/dist/mcp/tools/export-graph.d.ts.map +1 -1
- package/dist/mcp/tools/file-deps.d.ts +1 -1
- package/dist/mcp/tools/file-deps.d.ts.map +1 -1
- package/dist/mcp/tools/file-exports.d.ts +1 -1
- package/dist/mcp/tools/file-exports.d.ts.map +1 -1
- package/dist/mcp/tools/find-cycles.d.ts +1 -1
- package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
- package/dist/mcp/tools/fn-impact.d.ts +1 -1
- package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
- package/dist/mcp/tools/impact-analysis.d.ts +1 -1
- package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
- package/dist/mcp/tools/implementations.d.ts +1 -1
- package/dist/mcp/tools/implementations.d.ts.map +1 -1
- package/dist/mcp/tools/index.d.ts +2 -5
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/interfaces.d.ts +1 -1
- package/dist/mcp/tools/interfaces.d.ts.map +1 -1
- package/dist/mcp/tools/list-functions.d.ts +1 -1
- package/dist/mcp/tools/list-functions.d.ts.map +1 -1
- package/dist/mcp/tools/list-repos.d.ts +1 -1
- package/dist/mcp/tools/list-repos.d.ts.map +1 -1
- package/dist/mcp/tools/module-map.d.ts +1 -1
- package/dist/mcp/tools/module-map.d.ts.map +1 -1
- package/dist/mcp/tools/node-roles.d.ts +1 -1
- package/dist/mcp/tools/node-roles.d.ts.map +1 -1
- package/dist/mcp/tools/path.d.ts +1 -1
- package/dist/mcp/tools/path.d.ts.map +1 -1
- package/dist/mcp/tools/query.d.ts +1 -1
- package/dist/mcp/tools/query.d.ts.map +1 -1
- package/dist/mcp/tools/semantic-search.d.ts +1 -1
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
- package/dist/mcp/tools/sequence.d.ts +1 -1
- package/dist/mcp/tools/sequence.d.ts.map +1 -1
- package/dist/mcp/tools/structure.d.ts +1 -1
- package/dist/mcp/tools/structure.d.ts.map +1 -1
- package/dist/mcp/tools/symbol-children.d.ts +1 -1
- package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
- package/dist/mcp/tools/triage.d.ts +1 -1
- package/dist/mcp/tools/triage.d.ts.map +1 -1
- package/dist/mcp/tools/where.d.ts +1 -1
- package/dist/mcp/tools/where.d.ts.map +1 -1
- package/dist/mcp/types.d.ts +19 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +6 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/presentation/queries-cli/index.d.ts +1 -1
- package/dist/presentation/queries-cli/index.d.ts.map +1 -1
- package/dist/presentation/queries-cli/index.js +1 -1
- package/dist/presentation/queries-cli/index.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts +1 -0
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +20 -1
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli.d.ts +1 -1
- package/dist/presentation/queries-cli.d.ts.map +1 -1
- package/dist/presentation/queries-cli.js +1 -1
- package/dist/presentation/queries-cli.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +45 -32
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/constants.d.ts +21 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +25 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +12 -22
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +4 -17
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +76 -1
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +43 -63
- package/src/ast-analysis/rules/b2.ts +263 -0
- package/src/ast-analysis/rules/b3.ts +127 -0
- package/src/ast-analysis/rules/b4.ts +378 -0
- package/src/ast-analysis/rules/b5.ts +65 -0
- package/src/ast-analysis/rules/c.ts +157 -0
- package/src/ast-analysis/rules/index.ts +34 -0
- package/src/ast-analysis/rules/javascript.ts +3 -0
- package/src/ast-analysis/shared.ts +2 -0
- package/src/ast-analysis/visitor-utils.ts +5 -0
- package/src/ast-analysis/visitor.ts +82 -52
- package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
- package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
- package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
- package/src/cli/commands/config.ts +184 -184
- package/src/cli/commands/roles.ts +6 -1
- package/src/db/better-sqlite3.ts +5 -4
- package/src/db/connection.ts +23 -5
- package/src/db/index.ts +1 -0
- package/src/db/migrations.ts +68 -0
- package/src/db/repository/build-stmts.ts +30 -0
- package/src/db/repository/dataflow.ts +16 -0
- package/src/db/repository/index.ts +1 -1
- package/src/db/repository/native-repository.ts +56 -40
- package/src/domain/analysis/fn-impact.ts +4 -0
- package/src/domain/analysis/module-map.ts +38 -6
- package/src/domain/analysis/roles.ts +23 -0
- package/src/domain/graph/builder/call-resolver.ts +112 -232
- package/src/domain/graph/builder/context.ts +1 -0
- package/src/domain/graph/builder/helpers.ts +190 -72
- package/src/domain/graph/builder/incremental.ts +249 -120
- package/src/domain/graph/builder/pipeline.ts +11 -5
- package/src/domain/graph/builder/stages/build-edges.ts +696 -296
- package/src/domain/graph/builder/stages/collect-files.ts +12 -6
- package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
- package/src/domain/graph/builder/stages/native-orchestrator.ts +1102 -590
- package/src/domain/graph/resolver/points-to.ts +182 -59
- package/src/domain/graph/resolver/strategy.ts +265 -0
- package/src/domain/graph/watcher.ts +19 -9
- package/src/domain/parser.ts +12 -2
- package/src/domain/queries.ts +1 -1
- package/src/domain/wasm-worker-entry.ts +3 -0
- package/src/domain/wasm-worker-pool.ts +28 -4
- package/src/domain/wasm-worker-protocol.ts +4 -0
- package/src/extractors/dart.ts +48 -3
- package/src/extractors/groovy.ts +62 -2
- package/src/extractors/helpers.ts +5 -2
- package/src/extractors/java.ts +80 -1
- package/src/extractors/javascript.ts +566 -161
- package/src/extractors/kotlin.ts +57 -3
- package/src/extractors/objc.ts +25 -1
- package/src/extractors/scala.ts +63 -1
- package/src/extractors/swift.ts +46 -3
- package/src/features/audit.ts +43 -34
- package/src/features/boundaries.ts +17 -9
- package/src/features/cfg.ts +31 -22
- package/src/features/check.ts +21 -5
- package/src/features/communities.ts +28 -19
- package/src/features/dataflow.ts +755 -6
- package/src/features/manifesto.ts +76 -75
- package/src/features/sequence.ts +29 -23
- package/src/features/snapshot.ts +36 -25
- package/src/features/structure.ts +185 -55
- package/src/features/triage.ts +28 -15
- package/src/graph/algorithms/bfs.ts +13 -12
- package/src/graph/algorithms/tarjan.ts +5 -0
- package/src/graph/builders/dependency.ts +35 -23
- package/src/graph/classifiers/roles.ts +74 -7
- package/src/infrastructure/config.ts +32 -3
- package/src/infrastructure/registry.ts +44 -20
- package/src/infrastructure/update-check.ts +55 -33
- package/src/mcp/server.ts +2 -8
- package/src/mcp/tools/ast-query.ts +1 -1
- package/src/mcp/tools/audit.ts +1 -1
- package/src/mcp/tools/batch-query.ts +1 -1
- package/src/mcp/tools/branch-compare.ts +1 -1
- package/src/mcp/tools/brief.ts +1 -1
- package/src/mcp/tools/cfg.ts +1 -1
- package/src/mcp/tools/check.ts +1 -1
- package/src/mcp/tools/co-changes.ts +1 -1
- package/src/mcp/tools/code-owners.ts +1 -1
- package/src/mcp/tools/communities.ts +1 -1
- package/src/mcp/tools/complexity.ts +1 -1
- package/src/mcp/tools/context.ts +1 -1
- package/src/mcp/tools/dataflow.ts +1 -1
- package/src/mcp/tools/diff-impact.ts +1 -1
- package/src/mcp/tools/execution-flow.ts +1 -1
- package/src/mcp/tools/export-graph.ts +1 -1
- package/src/mcp/tools/file-deps.ts +1 -1
- package/src/mcp/tools/file-exports.ts +1 -1
- package/src/mcp/tools/find-cycles.ts +1 -1
- package/src/mcp/tools/fn-impact.ts +1 -1
- package/src/mcp/tools/impact-analysis.ts +1 -1
- package/src/mcp/tools/implementations.ts +1 -1
- package/src/mcp/tools/index.ts +2 -5
- package/src/mcp/tools/interfaces.ts +1 -1
- package/src/mcp/tools/list-functions.ts +1 -1
- package/src/mcp/tools/list-repos.ts +1 -1
- package/src/mcp/tools/module-map.ts +1 -1
- package/src/mcp/tools/node-roles.ts +1 -1
- package/src/mcp/tools/path.ts +1 -1
- 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/structure.ts +1 -1
- package/src/mcp/tools/symbol-children.ts +1 -1
- package/src/mcp/tools/triage.ts +1 -1
- package/src/mcp/tools/where.ts +1 -1
- package/src/mcp/types.ts +21 -0
- package/src/presentation/queries-cli/index.ts +1 -1
- package/src/presentation/queries-cli/overview.ts +35 -1
- package/src/presentation/queries-cli.ts +1 -0
- package/src/presentation/viewer.ts +98 -87
- package/src/shared/constants.ts +26 -0
- package/src/shared/normalize.ts +13 -22
- package/src/shared/paginate.ts +4 -18
- package/src/types.ts +86 -1
|
@@ -157,17 +157,27 @@ interface ManifestoOpts {
|
|
|
157
157
|
offset?: number;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
interface EvaluateRulesContext {
|
|
161
|
+
defs: RuleDef[];
|
|
162
|
+
rows: Array<Record<string, unknown>>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Shared rule-evaluation loop used by both evaluateFunctionRules and evaluateFileRules.
|
|
167
|
+
* Iterates rows, applies threshold checks for each active rule definition, and
|
|
168
|
+
* accumulates violations + per-rule worst-status tracking into ruleResults.
|
|
169
|
+
*/
|
|
170
|
+
function evaluateRules(
|
|
171
|
+
ctx: EvaluateRulesContext,
|
|
162
172
|
rules: ResolvedRules,
|
|
163
|
-
opts: ManifestoOpts,
|
|
164
173
|
violations: Violation[],
|
|
165
174
|
ruleResults: RuleResult[],
|
|
166
175
|
): void {
|
|
167
|
-
const
|
|
168
|
-
const activeDefs =
|
|
176
|
+
const { defs, rows } = ctx;
|
|
177
|
+
const activeDefs = defs.filter((d) => isEnabled(rules[d.name]!));
|
|
178
|
+
|
|
169
179
|
if (activeDefs.length === 0) {
|
|
170
|
-
for (const def of
|
|
180
|
+
for (const def of defs) {
|
|
171
181
|
ruleResults.push({
|
|
172
182
|
name: def.name,
|
|
173
183
|
level: def.level,
|
|
@@ -179,39 +189,9 @@ function evaluateFunctionRules(
|
|
|
179
189
|
return;
|
|
180
190
|
}
|
|
181
191
|
|
|
182
|
-
let where = "WHERE n.kind IN ('function','method')";
|
|
183
|
-
const params: unknown[] = [];
|
|
184
|
-
if (opts.noTests) where += NO_TEST_SQL;
|
|
185
|
-
{
|
|
186
|
-
const fc = buildFileConditionSQL(opts.file as string, 'n.file');
|
|
187
|
-
where += fc.sql;
|
|
188
|
-
params.push(...fc.params);
|
|
189
|
-
}
|
|
190
|
-
if (opts.kind) {
|
|
191
|
-
where += ' AND n.kind = ?';
|
|
192
|
-
params.push(opts.kind);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let rows: Array<Record<string, unknown>>;
|
|
196
|
-
try {
|
|
197
|
-
rows = db
|
|
198
|
-
.prepare(
|
|
199
|
-
`SELECT n.name, n.kind, n.file, n.line,
|
|
200
|
-
fc.cognitive, fc.cyclomatic, fc.max_nesting
|
|
201
|
-
FROM function_complexity fc
|
|
202
|
-
JOIN nodes n ON fc.node_id = n.id
|
|
203
|
-
${where}`,
|
|
204
|
-
)
|
|
205
|
-
.all(...params) as Array<Record<string, unknown>>;
|
|
206
|
-
} catch (err: unknown) {
|
|
207
|
-
debug(`manifesto function query failed: ${(err as Error).message}`);
|
|
208
|
-
rows = [];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Track worst status per rule
|
|
212
192
|
const worst: Record<string, string> = {};
|
|
213
193
|
const counts: Record<string, number> = {};
|
|
214
|
-
for (const def of
|
|
194
|
+
for (const def of defs) {
|
|
215
195
|
worst[def.name] = 'pass';
|
|
216
196
|
counts[def.name] = 0;
|
|
217
197
|
}
|
|
@@ -234,7 +214,7 @@ function evaluateFunctionRules(
|
|
|
234
214
|
}
|
|
235
215
|
}
|
|
236
216
|
|
|
237
|
-
for (const def of
|
|
217
|
+
for (const def of defs) {
|
|
238
218
|
ruleResults.push({
|
|
239
219
|
name: def.name,
|
|
240
220
|
level: def.level,
|
|
@@ -245,6 +225,60 @@ function evaluateFunctionRules(
|
|
|
245
225
|
}
|
|
246
226
|
}
|
|
247
227
|
|
|
228
|
+
function evaluateFunctionRules(
|
|
229
|
+
db: BetterSqlite3Database,
|
|
230
|
+
rules: ResolvedRules,
|
|
231
|
+
opts: ManifestoOpts,
|
|
232
|
+
violations: Violation[],
|
|
233
|
+
ruleResults: RuleResult[],
|
|
234
|
+
): void {
|
|
235
|
+
const defs = RULE_DEFS.filter((d) => d.level === 'function');
|
|
236
|
+
const activeDefs = defs.filter((d) => isEnabled(rules[d.name]!));
|
|
237
|
+
if (activeDefs.length === 0) {
|
|
238
|
+
for (const def of defs) {
|
|
239
|
+
ruleResults.push({
|
|
240
|
+
name: def.name,
|
|
241
|
+
level: def.level,
|
|
242
|
+
status: 'pass',
|
|
243
|
+
thresholds: rules[def.name]!,
|
|
244
|
+
violationCount: 0,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let where = "WHERE n.kind IN ('function','method')";
|
|
251
|
+
const params: unknown[] = [];
|
|
252
|
+
if (opts.noTests) where += NO_TEST_SQL;
|
|
253
|
+
{
|
|
254
|
+
const fc = buildFileConditionSQL(opts.file as string, 'n.file');
|
|
255
|
+
where += fc.sql;
|
|
256
|
+
params.push(...fc.params);
|
|
257
|
+
}
|
|
258
|
+
if (opts.kind) {
|
|
259
|
+
where += ' AND n.kind = ?';
|
|
260
|
+
params.push(opts.kind);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let rows: Array<Record<string, unknown>>;
|
|
264
|
+
try {
|
|
265
|
+
rows = db
|
|
266
|
+
.prepare(
|
|
267
|
+
`SELECT n.name, n.kind, n.file, n.line,
|
|
268
|
+
fc.cognitive, fc.cyclomatic, fc.max_nesting
|
|
269
|
+
FROM function_complexity fc
|
|
270
|
+
JOIN nodes n ON fc.node_id = n.id
|
|
271
|
+
${where}`,
|
|
272
|
+
)
|
|
273
|
+
.all(...params) as Array<Record<string, unknown>>;
|
|
274
|
+
} catch (err: unknown) {
|
|
275
|
+
debug(`manifesto function query failed: ${(err as Error).message}`);
|
|
276
|
+
rows = [];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
evaluateRules({ defs, rows }, rules, violations, ruleResults);
|
|
280
|
+
}
|
|
281
|
+
|
|
248
282
|
function evaluateFileRules(
|
|
249
283
|
db: BetterSqlite3Database,
|
|
250
284
|
rules: ResolvedRules,
|
|
@@ -252,10 +286,10 @@ function evaluateFileRules(
|
|
|
252
286
|
violations: Violation[],
|
|
253
287
|
ruleResults: RuleResult[],
|
|
254
288
|
): void {
|
|
255
|
-
const
|
|
256
|
-
const activeDefs =
|
|
289
|
+
const defs = RULE_DEFS.filter((d) => d.level === 'file');
|
|
290
|
+
const activeDefs = defs.filter((d) => isEnabled(rules[d.name]!));
|
|
257
291
|
if (activeDefs.length === 0) {
|
|
258
|
-
for (const def of
|
|
292
|
+
for (const def of defs) {
|
|
259
293
|
ruleResults.push({
|
|
260
294
|
name: def.name,
|
|
261
295
|
level: def.level,
|
|
@@ -293,40 +327,7 @@ function evaluateFileRules(
|
|
|
293
327
|
rows = [];
|
|
294
328
|
}
|
|
295
329
|
|
|
296
|
-
|
|
297
|
-
const counts: Record<string, number> = {};
|
|
298
|
-
for (const def of fileDefs) {
|
|
299
|
-
worst[def.name] = 'pass';
|
|
300
|
-
counts[def.name] = 0;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
for (const row of rows) {
|
|
304
|
-
for (const def of activeDefs) {
|
|
305
|
-
const value = row[def.metric] as number | null;
|
|
306
|
-
if (value == null) continue;
|
|
307
|
-
const meta = {
|
|
308
|
-
name: row.name as string,
|
|
309
|
-
file: row.file as string,
|
|
310
|
-
line: row.line as number,
|
|
311
|
-
};
|
|
312
|
-
const status = checkThreshold(def.name, rules[def.name]!, value, meta, violations);
|
|
313
|
-
if (status !== 'pass') {
|
|
314
|
-
counts[def.name] = (counts[def.name] ?? 0) + 1;
|
|
315
|
-
if (status === 'fail') worst[def.name] = 'fail';
|
|
316
|
-
else if (worst[def.name] !== 'fail') worst[def.name] = 'warn';
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
for (const def of fileDefs) {
|
|
322
|
-
ruleResults.push({
|
|
323
|
-
name: def.name,
|
|
324
|
-
level: def.level,
|
|
325
|
-
status: worst[def.name]!,
|
|
326
|
-
thresholds: rules[def.name]!,
|
|
327
|
-
violationCount: counts[def.name] ?? 0,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
+
evaluateRules({ defs, rows }, rules, violations, ruleResults);
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
function evaluateGraphRules(
|
package/src/features/sequence.ts
CHANGED
|
@@ -10,6 +10,34 @@ import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
|
|
|
10
10
|
|
|
11
11
|
// ─── Alias generation ────────────────────────────────────────────────
|
|
12
12
|
|
|
13
|
+
/** Build aliases for a group of paths that share the same basename.
|
|
14
|
+
* Progressively adds parent dirs until all aliases are unique. */
|
|
15
|
+
function resolveCollisionAliases(paths: string[], aliases: Map<string, string>): void {
|
|
16
|
+
for (let depth = 2; depth <= 10; depth++) {
|
|
17
|
+
const trial = new Map<string, string>();
|
|
18
|
+
let allUnique = true;
|
|
19
|
+
const seen = new Set<string>();
|
|
20
|
+
|
|
21
|
+
for (const p of paths) {
|
|
22
|
+
const parts = p.replace(/\.[^.]+$/, '').split('/');
|
|
23
|
+
const alias = parts
|
|
24
|
+
.slice(-depth)
|
|
25
|
+
.join('_')
|
|
26
|
+
.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
27
|
+
trial.set(p, alias);
|
|
28
|
+
if (seen.has(alias)) allUnique = false;
|
|
29
|
+
seen.add(alias);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (allUnique || depth === 10) {
|
|
33
|
+
for (const [p, alias] of trial) {
|
|
34
|
+
aliases.set(p, alias);
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
13
41
|
function buildAliases(files: string[]): Map<string, string> {
|
|
14
42
|
const aliases = new Map<string, string>();
|
|
15
43
|
const basenames = new Map<string, string[]>();
|
|
@@ -26,29 +54,7 @@ function buildAliases(files: string[]): Map<string, string> {
|
|
|
26
54
|
aliases.set(paths[0]!, base);
|
|
27
55
|
} else {
|
|
28
56
|
// Collision — progressively add parent dirs until aliases are unique
|
|
29
|
-
|
|
30
|
-
const trial = new Map<string, string>();
|
|
31
|
-
let allUnique = true;
|
|
32
|
-
const seen = new Set<string>();
|
|
33
|
-
|
|
34
|
-
for (const p of paths) {
|
|
35
|
-
const parts = p.replace(/\.[^.]+$/, '').split('/');
|
|
36
|
-
const alias = parts
|
|
37
|
-
.slice(-depth)
|
|
38
|
-
.join('_')
|
|
39
|
-
.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
40
|
-
trial.set(p, alias);
|
|
41
|
-
if (seen.has(alias)) allUnique = false;
|
|
42
|
-
seen.add(alias);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (allUnique || depth === 10) {
|
|
46
|
-
for (const [p, alias] of trial) {
|
|
47
|
-
aliases.set(p, alias);
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
57
|
+
resolveCollisionAliases(paths, aliases);
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|
package/src/features/snapshot.ts
CHANGED
|
@@ -25,6 +25,41 @@ interface SnapshotSaveOptions {
|
|
|
25
25
|
force?: boolean;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Atomically place `tmp` at `dest`.
|
|
30
|
+
* - force=true: renameSync (overwrites any existing file).
|
|
31
|
+
* - force=false: linkSync so EEXIST is detected atomically; then unlink tmp.
|
|
32
|
+
* Callers are responsible for cleaning up `tmp` on any thrown error.
|
|
33
|
+
*/
|
|
34
|
+
function atomicPlaceFile(tmp: string, dest: string, name: string, force?: boolean): void {
|
|
35
|
+
if (force) {
|
|
36
|
+
// renameSync overwrites atomically — the correct semantics for --force.
|
|
37
|
+
fs.renameSync(tmp, dest);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Non-force path: linkSync fails atomically with EEXIST if dest exists,
|
|
42
|
+
// closing the TOCTOU window between existsSync above and the final
|
|
43
|
+
// placement. We then unlink the temp file; on POSIX and NTFS, link
|
|
44
|
+
// creates a second reference so tmp can safely be removed.
|
|
45
|
+
try {
|
|
46
|
+
fs.linkSync(tmp, dest);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if ((err as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
49
|
+
throw new ConfigError(`Snapshot "${name}" already exists. Use --force to overwrite.`);
|
|
50
|
+
}
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
fs.unlinkSync(tmp);
|
|
55
|
+
} catch (cleanupErr) {
|
|
56
|
+
// Best-effort — dest is already in place, so a leftover tmp file is
|
|
57
|
+
// harmless. Log at debug so repeated failures surface during
|
|
58
|
+
// troubleshooting without noising up normal operation.
|
|
59
|
+
debug(`snapshotSave: failed to remove temp file ${tmp}: ${cleanupErr}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
28
63
|
export function snapshotSave(
|
|
29
64
|
name: string,
|
|
30
65
|
options: SnapshotSaveOptions = {},
|
|
@@ -73,31 +108,7 @@ export function snapshotSave(
|
|
|
73
108
|
}
|
|
74
109
|
|
|
75
110
|
try {
|
|
76
|
-
|
|
77
|
-
// renameSync overwrites atomically — the correct semantics for --force.
|
|
78
|
-
fs.renameSync(tmp, dest);
|
|
79
|
-
} else {
|
|
80
|
-
// Non-force path: linkSync fails atomically with EEXIST if dest exists,
|
|
81
|
-
// closing the TOCTOU window between existsSync above and the final
|
|
82
|
-
// placement. We then unlink the temp file; on POSIX and NTFS, link
|
|
83
|
-
// creates a second reference so tmp can safely be removed.
|
|
84
|
-
try {
|
|
85
|
-
fs.linkSync(tmp, dest);
|
|
86
|
-
} catch (err) {
|
|
87
|
-
if ((err as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
88
|
-
throw new ConfigError(`Snapshot "${name}" already exists. Use --force to overwrite.`);
|
|
89
|
-
}
|
|
90
|
-
throw err;
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
fs.unlinkSync(tmp);
|
|
94
|
-
} catch (cleanupErr) {
|
|
95
|
-
// Best-effort — dest is already in place, so a leftover tmp file is
|
|
96
|
-
// harmless. Log at debug so repeated failures surface during
|
|
97
|
-
// troubleshooting without noising up normal operation.
|
|
98
|
-
debug(`snapshotSave: failed to remove temp file ${tmp}: ${cleanupErr}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
111
|
+
atomicPlaceFile(tmp, dest, name, options.force);
|
|
101
112
|
} catch (err) {
|
|
102
113
|
try {
|
|
103
114
|
fs.unlinkSync(tmp);
|
|
@@ -90,6 +90,44 @@ interface SqliteStatement {
|
|
|
90
90
|
run(...params: unknown[]): unknown;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/** Insert file→parent-directory contains edges (incremental-aware). */
|
|
94
|
+
function insertFileToParentEdges(
|
|
95
|
+
insertEdge: SqliteStatement,
|
|
96
|
+
getNodeIdStmt: NodeIdStmt,
|
|
97
|
+
fileSymbols: Map<string, FileSymbolData>,
|
|
98
|
+
affectedDirs: Set<string> | null,
|
|
99
|
+
): void {
|
|
100
|
+
for (const relPath of fileSymbols.keys()) {
|
|
101
|
+
const dir = normalizePath(path.dirname(relPath));
|
|
102
|
+
if (!dir || dir === '.') continue;
|
|
103
|
+
if (affectedDirs && !affectedDirs.has(dir)) continue;
|
|
104
|
+
const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
105
|
+
const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
106
|
+
if (dirRow && fileRow) {
|
|
107
|
+
insertEdge.run(dirRow.id, fileRow.id, 'contains', 1.0, 0);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Insert child-directory→parent-directory contains edges (incremental-aware). */
|
|
113
|
+
function insertDirToParentEdges(
|
|
114
|
+
insertEdge: SqliteStatement,
|
|
115
|
+
getNodeIdStmt: NodeIdStmt,
|
|
116
|
+
allDirs: Set<string>,
|
|
117
|
+
affectedDirs: Set<string> | null,
|
|
118
|
+
): void {
|
|
119
|
+
for (const dir of allDirs) {
|
|
120
|
+
const parent = normalizePath(path.dirname(dir));
|
|
121
|
+
if (!parent || parent === '.' || parent === dir) continue;
|
|
122
|
+
if (affectedDirs && !affectedDirs.has(parent)) continue;
|
|
123
|
+
const parentRow = getNodeIdStmt.get(parent, 'directory', parent, 0);
|
|
124
|
+
const childRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
125
|
+
if (parentRow && childRow) {
|
|
126
|
+
insertEdge.run(parentRow.id, childRow.id, 'contains', 1.0, 0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
93
131
|
function insertContainsEdges(
|
|
94
132
|
db: BetterSqlite3Database,
|
|
95
133
|
insertEdge: SqliteStatement,
|
|
@@ -102,26 +140,8 @@ function insertContainsEdges(
|
|
|
102
140
|
const affectedDirs = isIncremental ? getAncestorDirs(changedFiles ?? []) : null;
|
|
103
141
|
|
|
104
142
|
db.transaction(() => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!dir || dir === '.') continue;
|
|
108
|
-
if (affectedDirs && !affectedDirs.has(dir)) continue;
|
|
109
|
-
const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
110
|
-
const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
111
|
-
if (dirRow && fileRow) {
|
|
112
|
-
insertEdge.run(dirRow.id, fileRow.id, 'contains', 1.0, 0);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
for (const dir of allDirs) {
|
|
116
|
-
const parent = normalizePath(path.dirname(dir));
|
|
117
|
-
if (!parent || parent === '.' || parent === dir) continue;
|
|
118
|
-
if (affectedDirs && !affectedDirs.has(parent)) continue;
|
|
119
|
-
const parentRow = getNodeIdStmt.get(parent, 'directory', parent, 0);
|
|
120
|
-
const childRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
121
|
-
if (parentRow && childRow) {
|
|
122
|
-
insertEdge.run(parentRow.id, childRow.id, 'contains', 1.0, 0);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
143
|
+
insertFileToParentEdges(insertEdge, getNodeIdStmt, fileSymbols, affectedDirs);
|
|
144
|
+
insertDirToParentEdges(insertEdge, getNodeIdStmt, allDirs, affectedDirs);
|
|
125
145
|
})();
|
|
126
146
|
}
|
|
127
147
|
|
|
@@ -249,40 +269,59 @@ function buildFileToAncestorDirs(dirFiles: Map<string, string[]>): Map<string, S
|
|
|
249
269
|
return fileToAncestorDirs;
|
|
250
270
|
}
|
|
251
271
|
|
|
272
|
+
/** Initialise a zero-count map for all known directories. */
|
|
273
|
+
function initDirEdgeCounts(
|
|
274
|
+
allDirs: Set<string>,
|
|
275
|
+
): Map<string, { intra: number; fanIn: number; fanOut: number }> {
|
|
276
|
+
const m = new Map<string, { intra: number; fanIn: number; fanOut: number }>();
|
|
277
|
+
for (const dir of allDirs) m.set(dir, { intra: 0, fanIn: 0, fanOut: 0 });
|
|
278
|
+
return m;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Accumulate source-side (intra / fanOut) counts for one import edge. */
|
|
282
|
+
function accumulateSrcDirCounts(
|
|
283
|
+
srcDirs: Set<string>,
|
|
284
|
+
tgtDirs: Set<string> | undefined,
|
|
285
|
+
dirEdgeCounts: Map<string, { intra: number; fanIn: number; fanOut: number }>,
|
|
286
|
+
): void {
|
|
287
|
+
for (const dir of srcDirs) {
|
|
288
|
+
const counts = dirEdgeCounts.get(dir);
|
|
289
|
+
if (!counts) continue;
|
|
290
|
+
if (tgtDirs?.has(dir)) {
|
|
291
|
+
counts.intra++;
|
|
292
|
+
} else {
|
|
293
|
+
counts.fanOut++;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** Accumulate target-side (fanIn) counts for one import edge. */
|
|
299
|
+
function accumulateTgtDirCounts(
|
|
300
|
+
tgtDirs: Set<string>,
|
|
301
|
+
srcDirs: Set<string> | undefined,
|
|
302
|
+
dirEdgeCounts: Map<string, { intra: number; fanIn: number; fanOut: number }>,
|
|
303
|
+
): void {
|
|
304
|
+
for (const dir of tgtDirs) {
|
|
305
|
+
if (srcDirs?.has(dir)) continue;
|
|
306
|
+
const counts = dirEdgeCounts.get(dir);
|
|
307
|
+
if (!counts) continue;
|
|
308
|
+
counts.fanIn++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
252
312
|
/** Count intra-directory, fan-in, and fan-out edges per directory. */
|
|
253
313
|
function countDirectoryEdges(
|
|
254
314
|
allDirs: Set<string>,
|
|
255
315
|
importEdges: ImportEdge[],
|
|
256
316
|
fileToAncestorDirs: Map<string, Set<string>>,
|
|
257
317
|
): Map<string, { intra: number; fanIn: number; fanOut: number }> {
|
|
258
|
-
const dirEdgeCounts =
|
|
259
|
-
for (const dir of allDirs) {
|
|
260
|
-
dirEdgeCounts.set(dir, { intra: 0, fanIn: 0, fanOut: 0 });
|
|
261
|
-
}
|
|
318
|
+
const dirEdgeCounts = initDirEdgeCounts(allDirs);
|
|
262
319
|
for (const { source_file, target_file } of importEdges) {
|
|
263
320
|
const srcDirs = fileToAncestorDirs.get(source_file);
|
|
264
321
|
const tgtDirs = fileToAncestorDirs.get(target_file);
|
|
265
322
|
if (!srcDirs && !tgtDirs) continue;
|
|
266
|
-
|
|
267
|
-
if (srcDirs)
|
|
268
|
-
for (const dir of srcDirs) {
|
|
269
|
-
const counts = dirEdgeCounts.get(dir);
|
|
270
|
-
if (!counts) continue;
|
|
271
|
-
if (tgtDirs?.has(dir)) {
|
|
272
|
-
counts.intra++;
|
|
273
|
-
} else {
|
|
274
|
-
counts.fanOut++;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (tgtDirs) {
|
|
279
|
-
for (const dir of tgtDirs) {
|
|
280
|
-
if (srcDirs?.has(dir)) continue;
|
|
281
|
-
const counts = dirEdgeCounts.get(dir);
|
|
282
|
-
if (!counts) continue;
|
|
283
|
-
counts.fanIn++;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
323
|
+
if (srcDirs) accumulateSrcDirCounts(srcDirs, tgtDirs, dirEdgeCounts);
|
|
324
|
+
if (tgtDirs) accumulateTgtDirCounts(tgtDirs, srcDirs, dirEdgeCounts);
|
|
286
325
|
}
|
|
287
326
|
return dirEdgeCounts;
|
|
288
327
|
}
|
|
@@ -541,15 +580,50 @@ interface CallableNodeRow {
|
|
|
541
580
|
fan_out: number;
|
|
542
581
|
}
|
|
543
582
|
|
|
544
|
-
/**
|
|
545
|
-
|
|
583
|
+
/**
|
|
584
|
+
* Kinds that are consumed via annotations/references rather than calls.
|
|
585
|
+
* These do not count as "active callables" for the hasActiveFileSiblings heuristic.
|
|
586
|
+
*/
|
|
587
|
+
const ANNOTATION_ONLY_KINDS = new Set([
|
|
588
|
+
'constant',
|
|
589
|
+
'struct',
|
|
590
|
+
'enum',
|
|
591
|
+
'trait',
|
|
592
|
+
'type',
|
|
593
|
+
'interface',
|
|
594
|
+
'record',
|
|
595
|
+
]);
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Build two active-files sets from callable rows:
|
|
599
|
+
*
|
|
600
|
+
* - `activeFiles`: files with at least one non-annotation-only callable with
|
|
601
|
+
* `fan_in > 0 || fan_out > 0`. Used for annotation-only kinds (constants,
|
|
602
|
+
* type defs) which have no callers by design.
|
|
603
|
+
*
|
|
604
|
+
* - `calledActiveFiles`: files with at least one non-annotation-only callable
|
|
605
|
+
* with `fan_in > 0` (strictly called). Used for method/function kinds to
|
|
606
|
+
* prevent a self-sibling loop: a function with `fanIn=0, fanOut>0` as the
|
|
607
|
+
* only callable in its file must NOT count itself as an "active sibling" and
|
|
608
|
+
* thus promote itself to `leaf`.
|
|
609
|
+
*/
|
|
610
|
+
function buildActiveFilesSet(rows: CallableNodeRow[]): {
|
|
611
|
+
activeFiles: Set<string>;
|
|
612
|
+
calledActiveFiles: Set<string>;
|
|
613
|
+
} {
|
|
546
614
|
const activeFiles = new Set<string>();
|
|
615
|
+
const calledActiveFiles = new Set<string>();
|
|
547
616
|
for (const r of rows) {
|
|
548
|
-
if ((r.
|
|
549
|
-
|
|
617
|
+
if (!ANNOTATION_ONLY_KINDS.has(r.kind)) {
|
|
618
|
+
if (r.fan_in > 0 || r.fan_out > 0) {
|
|
619
|
+
activeFiles.add(r.file);
|
|
620
|
+
}
|
|
621
|
+
if (r.fan_in > 0) {
|
|
622
|
+
calledActiveFiles.add(r.file);
|
|
623
|
+
}
|
|
550
624
|
}
|
|
551
625
|
}
|
|
552
|
-
return activeFiles;
|
|
626
|
+
return { activeFiles, calledActiveFiles };
|
|
553
627
|
}
|
|
554
628
|
|
|
555
629
|
/** Map callable rows to classifier input objects, attaching exported/prod-fan-in/active-file metadata. */
|
|
@@ -558,6 +632,7 @@ function buildClassifierInput(
|
|
|
558
632
|
exportedIds: Set<number>,
|
|
559
633
|
prodFanInMap: Map<number, number>,
|
|
560
634
|
activeFiles: Set<string>,
|
|
635
|
+
calledActiveFiles: Set<string>,
|
|
561
636
|
): Array<{
|
|
562
637
|
id: string;
|
|
563
638
|
name: string;
|
|
@@ -578,7 +653,20 @@ function buildClassifierInput(
|
|
|
578
653
|
fanOut: r.fan_out,
|
|
579
654
|
isExported: exportedIds.has(r.id),
|
|
580
655
|
productionFanIn: prodFanInMap.get(r.id) || 0,
|
|
581
|
-
hasActiveFileSiblings
|
|
656
|
+
// Set hasActiveFileSiblings for annotation-only kinds (constants, type defs)
|
|
657
|
+
// AND for method/function — the latter two can have fanIn === 0 due to
|
|
658
|
+
// untraced call-site patterns (interface dispatch, logical-or defaults).
|
|
659
|
+
// The classifier interprets this field differently per kind (see classifyUnreferencedNode).
|
|
660
|
+
//
|
|
661
|
+
// IMPORTANT: method/function use calledActiveFiles (fan_in > 0 only) to
|
|
662
|
+
// prevent a self-sibling false negative: a function with fanIn=0, fanOut>0
|
|
663
|
+
// as the sole callable in its file must NOT see its own file as "active"
|
|
664
|
+
// and promote itself to leaf.
|
|
665
|
+
hasActiveFileSiblings: ANNOTATION_ONLY_KINDS.has(r.kind)
|
|
666
|
+
? activeFiles.has(r.file)
|
|
667
|
+
: r.kind === 'method' || r.kind === 'function'
|
|
668
|
+
? calledActiveFiles.has(r.file)
|
|
669
|
+
: undefined,
|
|
582
670
|
}));
|
|
583
671
|
}
|
|
584
672
|
|
|
@@ -660,7 +748,8 @@ function readCachedMedians(db: BetterSqlite3Database): { fanIn: number; fanOut:
|
|
|
660
748
|
)
|
|
661
749
|
return null;
|
|
662
750
|
return { fanIn: cached.fanIn, fanOut: cached.fanOut };
|
|
663
|
-
} catch {
|
|
751
|
+
} catch (e) {
|
|
752
|
+
debug(`readCachedMedians: failed to parse cached medians — ${e}`);
|
|
664
753
|
return null;
|
|
665
754
|
}
|
|
666
755
|
}
|
|
@@ -761,6 +850,20 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm
|
|
|
761
850
|
.all() as { id: number }[];
|
|
762
851
|
for (const r of reexportExported) exportedIds.add(r.id);
|
|
763
852
|
|
|
853
|
+
// Mark symbols with exported=1 as exported — the extractor sets this flag when the
|
|
854
|
+
// author writes `export interface Foo { }` / `export type Bar = ...` / `export function`.
|
|
855
|
+
// Cross-file edge inference misses these when the symbol is only used as a type annotation
|
|
856
|
+
// within the same file (no calls/imports-type edge is produced for same-file type usage).
|
|
857
|
+
// This fixes false dead-unresolved classification for exported interfaces with no external callers (#1583).
|
|
858
|
+
const explicitlyExported = db
|
|
859
|
+
.prepare(
|
|
860
|
+
`SELECT id FROM nodes
|
|
861
|
+
WHERE exported = 1
|
|
862
|
+
AND kind NOT IN ('file', 'directory', 'parameter', 'property')`,
|
|
863
|
+
)
|
|
864
|
+
.all() as { id: number }[];
|
|
865
|
+
for (const r of explicitlyExported) exportedIds.add(r.id);
|
|
866
|
+
|
|
764
867
|
// Compute production fan-in (excluding callers in test files)
|
|
765
868
|
const prodFanInMap = new Map<number, number>();
|
|
766
869
|
const prodRows = db
|
|
@@ -781,8 +884,14 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm
|
|
|
781
884
|
// Compute medians from the already-loaded rows (no extra DB round-trip),
|
|
782
885
|
// pass them as overrides to avoid recomputing inside classifyRoles,
|
|
783
886
|
// and cache them for subsequent incremental builds.
|
|
784
|
-
const activeFiles = buildActiveFilesSet(rows);
|
|
785
|
-
const classifierInput = buildClassifierInput(
|
|
887
|
+
const { activeFiles, calledActiveFiles } = buildActiveFilesSet(rows);
|
|
888
|
+
const classifierInput = buildClassifierInput(
|
|
889
|
+
rows,
|
|
890
|
+
exportedIds,
|
|
891
|
+
prodFanInMap,
|
|
892
|
+
activeFiles,
|
|
893
|
+
calledActiveFiles,
|
|
894
|
+
);
|
|
786
895
|
const nonZeroFanIn = classifierInput
|
|
787
896
|
.filter((n) => n.fanIn > 0)
|
|
788
897
|
.map((n) => n.fanIn)
|
|
@@ -928,6 +1037,21 @@ function classifyNodeRolesIncremental(
|
|
|
928
1037
|
.all(...allAffectedFiles) as { id: number }[];
|
|
929
1038
|
for (const r of reexportExported) exportedIds.add(r.id);
|
|
930
1039
|
|
|
1040
|
+
// 3c. Mark symbols with exported=1 as exported — the extractor sets this flag when the
|
|
1041
|
+
// author writes `export interface Foo { }` / `export type Bar = ...` / `export function`.
|
|
1042
|
+
// Cross-file edge inference misses these when the symbol is only used as a type annotation
|
|
1043
|
+
// within the same file (no calls/imports-type edge is produced for same-file type usage).
|
|
1044
|
+
// Scoped to affected files only for the incremental path (#1583).
|
|
1045
|
+
const explicitlyExported = db
|
|
1046
|
+
.prepare(
|
|
1047
|
+
`SELECT id FROM nodes
|
|
1048
|
+
WHERE exported = 1
|
|
1049
|
+
AND kind NOT IN ('file', 'directory', 'parameter', 'property')
|
|
1050
|
+
AND file IN (${placeholders})`,
|
|
1051
|
+
)
|
|
1052
|
+
.all(...allAffectedFiles) as { id: number }[];
|
|
1053
|
+
for (const r of explicitlyExported) exportedIds.add(r.id);
|
|
1054
|
+
|
|
931
1055
|
// 4. Production fan-in for affected nodes only
|
|
932
1056
|
const prodFanInMap = new Map<number, number>();
|
|
933
1057
|
const prodRows = db
|
|
@@ -947,8 +1071,14 @@ function classifyNodeRolesIncremental(
|
|
|
947
1071
|
}
|
|
948
1072
|
|
|
949
1073
|
// 5. Classify affected nodes using global medians
|
|
950
|
-
const activeFiles = buildActiveFilesSet(rows);
|
|
951
|
-
const classifierInput = buildClassifierInput(
|
|
1074
|
+
const { activeFiles, calledActiveFiles } = buildActiveFilesSet(rows);
|
|
1075
|
+
const classifierInput = buildClassifierInput(
|
|
1076
|
+
rows,
|
|
1077
|
+
exportedIds,
|
|
1078
|
+
prodFanInMap,
|
|
1079
|
+
activeFiles,
|
|
1080
|
+
calledActiveFiles,
|
|
1081
|
+
);
|
|
952
1082
|
const roleMap = classifyRoles(classifierInput, globalMedians);
|
|
953
1083
|
|
|
954
1084
|
// 6. Build summary (only for affected nodes) and update only those nodes
|