optave-codegraph 3.13.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/LICENSE +190 -0
- package/README.md +984 -0
- package/dist/ast-analysis/engine.d.ts +19 -0
- package/dist/ast-analysis/engine.d.ts.map +1 -0
- package/dist/ast-analysis/engine.js +660 -0
- package/dist/ast-analysis/engine.js.map +1 -0
- package/dist/ast-analysis/metrics.d.ts +34 -0
- package/dist/ast-analysis/metrics.d.ts.map +1 -0
- package/dist/ast-analysis/metrics.js +123 -0
- package/dist/ast-analysis/metrics.js.map +1 -0
- 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/csharp.d.ts +7 -0
- package/dist/ast-analysis/rules/csharp.d.ts.map +1 -0
- package/dist/ast-analysis/rules/csharp.js +196 -0
- package/dist/ast-analysis/rules/csharp.js.map +1 -0
- package/dist/ast-analysis/rules/go.d.ts +7 -0
- package/dist/ast-analysis/rules/go.d.ts.map +1 -0
- package/dist/ast-analysis/rules/go.js +173 -0
- package/dist/ast-analysis/rules/go.js.map +1 -0
- package/dist/ast-analysis/rules/index.d.ts +13 -0
- package/dist/ast-analysis/rules/index.d.ts.map +1 -0
- package/dist/ast-analysis/rules/index.js +325 -0
- package/dist/ast-analysis/rules/index.js.map +1 -0
- package/dist/ast-analysis/rules/java.d.ts +7 -0
- package/dist/ast-analysis/rules/java.d.ts.map +1 -0
- package/dist/ast-analysis/rules/java.js +167 -0
- package/dist/ast-analysis/rules/java.js.map +1 -0
- package/dist/ast-analysis/rules/javascript.d.ts +7 -0
- package/dist/ast-analysis/rules/javascript.d.ts.map +1 -0
- package/dist/ast-analysis/rules/javascript.js +233 -0
- package/dist/ast-analysis/rules/javascript.js.map +1 -0
- package/dist/ast-analysis/rules/php.d.ts +7 -0
- package/dist/ast-analysis/rules/php.d.ts.map +1 -0
- package/dist/ast-analysis/rules/php.js +212 -0
- package/dist/ast-analysis/rules/php.js.map +1 -0
- package/dist/ast-analysis/rules/python.d.ts +7 -0
- package/dist/ast-analysis/rules/python.d.ts.map +1 -0
- package/dist/ast-analysis/rules/python.js +189 -0
- package/dist/ast-analysis/rules/python.js.map +1 -0
- package/dist/ast-analysis/rules/ruby.d.ts +7 -0
- package/dist/ast-analysis/rules/ruby.d.ts.map +1 -0
- package/dist/ast-analysis/rules/ruby.js +193 -0
- package/dist/ast-analysis/rules/ruby.js.map +1 -0
- package/dist/ast-analysis/rules/rust.d.ts +7 -0
- package/dist/ast-analysis/rules/rust.d.ts.map +1 -0
- package/dist/ast-analysis/rules/rust.js +166 -0
- package/dist/ast-analysis/rules/rust.js.map +1 -0
- package/dist/ast-analysis/shared.d.ts +12 -0
- package/dist/ast-analysis/shared.d.ts.map +1 -0
- package/dist/ast-analysis/shared.js +166 -0
- package/dist/ast-analysis/shared.js.map +1 -0
- package/dist/ast-analysis/visitor-utils.d.ts +70 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -0
- package/dist/ast-analysis/visitor-utils.js +235 -0
- package/dist/ast-analysis/visitor-utils.js.map +1 -0
- package/dist/ast-analysis/visitor.d.ts +27 -0
- package/dist/ast-analysis/visitor.d.ts.map +1 -0
- package/dist/ast-analysis/visitor.js +178 -0
- package/dist/ast-analysis/visitor.js.map +1 -0
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts +4 -0
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/ast-store-visitor.js +288 -0
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -0
- 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 +5 -0
- package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/cfg-visitor.js +231 -0
- package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -0
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts +8 -0
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/complexity-visitor.js +233 -0
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -0
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts +5 -0
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -0
- package/dist/ast-analysis/visitors/dataflow-visitor.js +378 -0
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -0
- package/dist/cli/commands/ast.d.ts +3 -0
- package/dist/cli/commands/ast.d.ts.map +1 -0
- package/dist/cli/commands/ast.js +23 -0
- package/dist/cli/commands/ast.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +3 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +47 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/batch.d.ts +3 -0
- package/dist/cli/commands/batch.d.ts.map +1 -0
- package/dist/cli/commands/batch.js +66 -0
- package/dist/cli/commands/batch.js.map +1 -0
- package/dist/cli/commands/branch-compare.d.ts +3 -0
- package/dist/cli/commands/branch-compare.d.ts.map +1 -0
- package/dist/cli/commands/branch-compare.js +26 -0
- package/dist/cli/commands/branch-compare.js.map +1 -0
- package/dist/cli/commands/brief.d.ts +3 -0
- package/dist/cli/commands/brief.d.ts.map +1 -0
- package/dist/cli/commands/brief.js +12 -0
- package/dist/cli/commands/brief.js.map +1 -0
- package/dist/cli/commands/build.d.ts +3 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +33 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/cfg.d.ts +3 -0
- package/dist/cli/commands/cfg.d.ts.map +1 -0
- package/dist/cli/commands/cfg.js +27 -0
- package/dist/cli/commands/cfg.js.map +1 -0
- package/dist/cli/commands/check.d.ts +3 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +65 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/children.d.ts +3 -0
- package/dist/cli/commands/children.d.ts.map +1 -0
- package/dist/cli/commands/children.js +33 -0
- package/dist/cli/commands/children.js.map +1 -0
- package/dist/cli/commands/co-change.d.ts +3 -0
- package/dist/cli/commands/co-change.d.ts.map +1 -0
- package/dist/cli/commands/co-change.js +66 -0
- package/dist/cli/commands/co-change.js.map +1 -0
- package/dist/cli/commands/communities.d.ts +3 -0
- package/dist/cli/commands/communities.d.ts.map +1 -0
- package/dist/cli/commands/communities.js +20 -0
- package/dist/cli/commands/communities.js.map +1 -0
- package/dist/cli/commands/complexity.d.ts +3 -0
- package/dist/cli/commands/complexity.d.ts.map +1 -0
- package/dist/cli/commands/complexity.js +47 -0
- package/dist/cli/commands/complexity.js.map +1 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +275 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/context.d.ts +3 -0
- package/dist/cli/commands/context.d.ts.map +1 -0
- package/dist/cli/commands/context.js +35 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/cycles.d.ts +3 -0
- package/dist/cli/commands/cycles.d.ts.map +1 -0
- package/dist/cli/commands/cycles.js +33 -0
- package/dist/cli/commands/cycles.js.map +1 -0
- package/dist/cli/commands/dataflow.d.ts +3 -0
- package/dist/cli/commands/dataflow.d.ts.map +1 -0
- package/dist/cli/commands/dataflow.js +29 -0
- package/dist/cli/commands/dataflow.js.map +1 -0
- package/dist/cli/commands/deps.d.ts +3 -0
- package/dist/cli/commands/deps.d.ts.map +1 -0
- package/dist/cli/commands/deps.js +18 -0
- package/dist/cli/commands/deps.js.map +1 -0
- package/dist/cli/commands/diff-impact.d.ts +3 -0
- package/dist/cli/commands/diff-impact.d.ts.map +1 -0
- package/dist/cli/commands/diff-impact.js +29 -0
- package/dist/cli/commands/diff-impact.js.map +1 -0
- package/dist/cli/commands/embed.d.ts +3 -0
- package/dist/cli/commands/embed.d.ts.map +1 -0
- package/dist/cli/commands/embed.js +75 -0
- package/dist/cli/commands/embed.js.map +1 -0
- package/dist/cli/commands/export.d.ts +3 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +72 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/exports.d.ts +3 -0
- package/dist/cli/commands/exports.d.ts.map +1 -0
- package/dist/cli/commands/exports.js +14 -0
- package/dist/cli/commands/exports.js.map +1 -0
- package/dist/cli/commands/flow.d.ts +3 -0
- package/dist/cli/commands/flow.d.ts.map +1 -0
- package/dist/cli/commands/flow.js +32 -0
- package/dist/cli/commands/flow.js.map +1 -0
- package/dist/cli/commands/fn-impact.d.ts +3 -0
- package/dist/cli/commands/fn-impact.d.ts.map +1 -0
- package/dist/cli/commands/fn-impact.js +33 -0
- package/dist/cli/commands/fn-impact.js.map +1 -0
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +12 -0
- package/dist/cli/commands/impact.js.map +1 -0
- package/dist/cli/commands/implementations.d.ts +3 -0
- package/dist/cli/commands/implementations.d.ts.map +1 -0
- package/dist/cli/commands/implementations.js +29 -0
- package/dist/cli/commands/implementations.js.map +1 -0
- package/dist/cli/commands/info.d.ts +3 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +67 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/interfaces.d.ts +3 -0
- package/dist/cli/commands/interfaces.d.ts.map +1 -0
- package/dist/cli/commands/interfaces.js +29 -0
- package/dist/cli/commands/interfaces.js.map +1 -0
- package/dist/cli/commands/map.d.ts +3 -0
- package/dist/cli/commands/map.d.ts.map +1 -0
- package/dist/cli/commands/map.js +19 -0
- package/dist/cli/commands/map.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +18 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/models.d.ts +3 -0
- package/dist/cli/commands/models.d.ts.map +1 -0
- package/dist/cli/commands/models.js +19 -0
- package/dist/cli/commands/models.js.map +1 -0
- package/dist/cli/commands/owners.d.ts +3 -0
- package/dist/cli/commands/owners.d.ts.map +1 -0
- package/dist/cli/commands/owners.js +27 -0
- package/dist/cli/commands/owners.js.map +1 -0
- package/dist/cli/commands/path.d.ts +3 -0
- package/dist/cli/commands/path.d.ts.map +1 -0
- package/dist/cli/commands/path.js +41 -0
- package/dist/cli/commands/path.js.map +1 -0
- package/dist/cli/commands/plot.d.ts +3 -0
- package/dist/cli/commands/plot.d.ts.map +1 -0
- package/dist/cli/commands/plot.js +96 -0
- package/dist/cli/commands/plot.js.map +1 -0
- package/dist/cli/commands/query.d.ts +3 -0
- package/dist/cli/commands/query.d.ts.map +1 -0
- package/dist/cli/commands/query.js +51 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/registry.d.ts +3 -0
- package/dist/cli/commands/registry.d.ts.map +1 -0
- package/dist/cli/commands/registry.js +95 -0
- package/dist/cli/commands/registry.js.map +1 -0
- package/dist/cli/commands/roles.d.ts +3 -0
- package/dist/cli/commands/roles.d.ts.map +1 -0
- package/dist/cli/commands/roles.js +36 -0
- package/dist/cli/commands/roles.js.map +1 -0
- package/dist/cli/commands/search.d.ts +3 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +43 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/sequence.d.ts +3 -0
- package/dist/cli/commands/sequence.d.ts.map +1 -0
- package/dist/cli/commands/sequence.js +29 -0
- package/dist/cli/commands/sequence.js.map +1 -0
- package/dist/cli/commands/snapshot.d.ts +3 -0
- package/dist/cli/commands/snapshot.d.ts.map +1 -0
- package/dist/cli/commands/snapshot.js +61 -0
- package/dist/cli/commands/snapshot.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +3 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +15 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/structure.d.ts +3 -0
- package/dist/cli/commands/structure.d.ts.map +1 -0
- package/dist/cli/commands/structure.js +50 -0
- package/dist/cli/commands/structure.js.map +1 -0
- package/dist/cli/commands/triage.d.ts +3 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +89 -0
- package/dist/cli/commands/triage.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +3 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +28 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/commands/where.d.ts +3 -0
- package/dist/cli/commands/where.d.ts.map +1 -0
- package/dist/cli/commands/where.js +20 -0
- package/dist/cli/commands/where.js.map +1 -0
- package/dist/cli/index.d.ts +22 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +139 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/shared/open-graph.d.ts +11 -0
- package/dist/cli/shared/open-graph.d.ts.map +1 -0
- package/dist/cli/shared/open-graph.js +9 -0
- package/dist/cli/shared/open-graph.js.map +1 -0
- package/dist/cli/shared/options.d.ts +25 -0
- package/dist/cli/shared/options.d.ts.map +1 -0
- package/dist/cli/shared/options.js +66 -0
- package/dist/cli/shared/options.js.map +1 -0
- package/dist/cli/shared/output.d.ts +2 -0
- package/dist/cli/shared/output.d.ts.map +1 -0
- package/dist/cli/shared/output.js +2 -0
- package/dist/cli/shared/output.js.map +1 -0
- package/dist/cli/types.d.ts +32 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -0
- package/dist/db/better-sqlite3.d.ts +4 -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 +82 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +444 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +6 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations.d.ts +11 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +416 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/query-builder.d.ts +88 -0
- package/dist/db/query-builder.d.ts.map +1 -0
- package/dist/db/query-builder.js +321 -0
- package/dist/db/query-builder.js.map +1 -0
- package/dist/db/repository/base.d.ts +103 -0
- package/dist/db/repository/base.d.ts.map +1 -0
- package/dist/db/repository/base.js +172 -0
- package/dist/db/repository/base.js.map +1 -0
- package/dist/db/repository/build-stmts.d.ts +16 -0
- package/dist/db/repository/build-stmts.d.ts.map +1 -0
- package/dist/db/repository/build-stmts.js +76 -0
- package/dist/db/repository/build-stmts.js.map +1 -0
- package/dist/db/repository/cached-stmt.d.ts +9 -0
- package/dist/db/repository/cached-stmt.d.ts.map +1 -0
- package/dist/db/repository/cached-stmt.js +15 -0
- package/dist/db/repository/cached-stmt.js.map +1 -0
- package/dist/db/repository/cfg.d.ts +34 -0
- package/dist/db/repository/cfg.d.ts.map +1 -0
- package/dist/db/repository/cfg.js +46 -0
- package/dist/db/repository/cfg.js.map +1 -0
- package/dist/db/repository/cochange.d.ts +14 -0
- package/dist/db/repository/cochange.d.ts.map +1 -0
- package/dist/db/repository/cochange.js +38 -0
- package/dist/db/repository/cochange.js.map +1 -0
- package/dist/db/repository/complexity.d.ts +7 -0
- package/dist/db/repository/complexity.d.ts.map +1 -0
- package/dist/db/repository/complexity.js +12 -0
- package/dist/db/repository/complexity.js.map +1 -0
- package/dist/db/repository/dataflow.d.ts +11 -0
- package/dist/db/repository/dataflow.d.ts.map +1 -0
- package/dist/db/repository/dataflow.js +30 -0
- package/dist/db/repository/dataflow.js.map +1 -0
- package/dist/db/repository/edges.d.ts +66 -0
- package/dist/db/repository/edges.d.ts.map +1 -0
- package/dist/db/repository/edges.js +175 -0
- package/dist/db/repository/edges.js.map +1 -0
- package/dist/db/repository/embeddings.d.ts +14 -0
- package/dist/db/repository/embeddings.d.ts.map +1 -0
- package/dist/db/repository/embeddings.js +40 -0
- package/dist/db/repository/embeddings.js.map +1 -0
- package/dist/db/repository/graph-read.d.ts +18 -0
- package/dist/db/repository/graph-read.d.ts.map +1 -0
- package/dist/db/repository/graph-read.js +33 -0
- package/dist/db/repository/graph-read.js.map +1 -0
- package/dist/db/repository/in-memory-repository.d.ts +88 -0
- package/dist/db/repository/in-memory-repository.d.ts.map +1 -0
- package/dist/db/repository/in-memory-repository.js +541 -0
- package/dist/db/repository/in-memory-repository.js.map +1 -0
- package/dist/db/repository/index.d.ts +16 -0
- package/dist/db/repository/index.d.ts.map +1 -0
- package/dist/db/repository/index.js +16 -0
- package/dist/db/repository/index.js.map +1 -0
- package/dist/db/repository/native-repository.d.ts +70 -0
- package/dist/db/repository/native-repository.d.ts.map +1 -0
- package/dist/db/repository/native-repository.js +418 -0
- package/dist/db/repository/native-repository.js.map +1 -0
- package/dist/db/repository/nodes.d.ts +86 -0
- package/dist/db/repository/nodes.d.ts.map +1 -0
- package/dist/db/repository/nodes.js +203 -0
- package/dist/db/repository/nodes.js.map +1 -0
- package/dist/db/repository/sqlite-repository.d.ts +59 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -0
- package/dist/db/repository/sqlite-repository.js +199 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -0
- package/dist/domain/analysis/brief.d.ts +25 -0
- package/dist/domain/analysis/brief.d.ts.map +1 -0
- package/dist/domain/analysis/brief.js +136 -0
- package/dist/domain/analysis/brief.js.map +1 -0
- package/dist/domain/analysis/context.d.ts +241 -0
- package/dist/domain/analysis/context.d.ts.map +1 -0
- package/dist/domain/analysis/context.js +361 -0
- package/dist/domain/analysis/context.js.map +1 -0
- package/dist/domain/analysis/dependencies.d.ts +158 -0
- package/dist/domain/analysis/dependencies.d.ts.map +1 -0
- package/dist/domain/analysis/dependencies.js +522 -0
- package/dist/domain/analysis/dependencies.js.map +1 -0
- package/dist/domain/analysis/diff-impact.d.ts +90 -0
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -0
- package/dist/domain/analysis/diff-impact.js +301 -0
- package/dist/domain/analysis/diff-impact.js.map +1 -0
- package/dist/domain/analysis/exports.d.ts +8 -0
- package/dist/domain/analysis/exports.d.ts.map +1 -0
- package/dist/domain/analysis/exports.js +178 -0
- package/dist/domain/analysis/exports.js.map +1 -0
- package/dist/domain/analysis/fn-impact.d.ts +61 -0
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -0
- package/dist/domain/analysis/fn-impact.js +190 -0
- package/dist/domain/analysis/fn-impact.js.map +1 -0
- package/dist/domain/analysis/impact.d.ts +11 -0
- package/dist/domain/analysis/impact.d.ts.map +1 -0
- package/dist/domain/analysis/impact.js +11 -0
- package/dist/domain/analysis/impact.js.map +1 -0
- package/dist/domain/analysis/implementations.d.ts +59 -0
- package/dist/domain/analysis/implementations.d.ts.map +1 -0
- package/dist/domain/analysis/implementations.js +65 -0
- package/dist/domain/analysis/implementations.js.map +1 -0
- package/dist/domain/analysis/module-map.d.ts +86 -0
- package/dist/domain/analysis/module-map.d.ts.map +1 -0
- package/dist/domain/analysis/module-map.js +468 -0
- package/dist/domain/analysis/module-map.js.map +1 -0
- package/dist/domain/analysis/query-helpers.d.ts +27 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
- package/dist/domain/analysis/query-helpers.js +41 -0
- package/dist/domain/analysis/query-helpers.js.map +1 -0
- package/dist/domain/analysis/roles.d.ts +20 -0
- package/dist/domain/analysis/roles.d.ts.map +1 -0
- package/dist/domain/analysis/roles.js +74 -0
- package/dist/domain/analysis/roles.js.map +1 -0
- package/dist/domain/analysis/symbol-lookup.d.ts +125 -0
- package/dist/domain/analysis/symbol-lookup.d.ts.map +1 -0
- package/dist/domain/analysis/symbol-lookup.js +206 -0
- package/dist/domain/analysis/symbol-lookup.js.map +1 -0
- package/dist/domain/graph/builder/call-resolver.d.ts +89 -0
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -0
- package/dist/domain/graph/builder/call-resolver.js +212 -0
- package/dist/domain/graph/builder/call-resolver.js.map +1 -0
- package/dist/domain/graph/builder/cha.d.ts +69 -0
- package/dist/domain/graph/builder/cha.d.ts.map +1 -0
- package/dist/domain/graph/builder/cha.js +158 -0
- package/dist/domain/graph/builder/cha.js.map +1 -0
- package/dist/domain/graph/builder/context.d.ts +91 -0
- package/dist/domain/graph/builder/context.d.ts.map +1 -0
- package/dist/domain/graph/builder/context.js +66 -0
- package/dist/domain/graph/builder/context.js.map +1 -0
- package/dist/domain/graph/builder/helpers.d.ts +97 -0
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -0
- package/dist/domain/graph/builder/helpers.js +558 -0
- package/dist/domain/graph/builder/helpers.js.map +1 -0
- package/dist/domain/graph/builder/incremental.d.ts +53 -0
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -0
- package/dist/domain/graph/builder/incremental.js +647 -0
- package/dist/domain/graph/builder/incremental.js.map +1 -0
- package/dist/domain/graph/builder/native-db-proxy.d.ts +24 -0
- package/dist/domain/graph/builder/native-db-proxy.d.ts.map +1 -0
- package/dist/domain/graph/builder/native-db-proxy.js +91 -0
- package/dist/domain/graph/builder/native-db-proxy.js.map +1 -0
- package/dist/domain/graph/builder/pipeline.d.ts +9 -0
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -0
- package/dist/domain/graph/builder/pipeline.js +448 -0
- package/dist/domain/graph/builder/pipeline.js.map +1 -0
- package/dist/domain/graph/builder/stages/build-edges.d.ts +3 -0
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/build-edges.js +1425 -0
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -0
- package/dist/domain/graph/builder/stages/build-structure.d.ts +3 -0
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/build-structure.js +250 -0
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -0
- package/dist/domain/graph/builder/stages/collect-files.d.ts +3 -0
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/collect-files.js +161 -0
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -0
- package/dist/domain/graph/builder/stages/detect-changes.d.ts +27 -0
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/detect-changes.js +593 -0
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -0
- package/dist/domain/graph/builder/stages/finalize.d.ts +3 -0
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/finalize.js +274 -0
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -0
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts +35 -0
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/insert-nodes.js +351 -0
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js +1763 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
- package/dist/domain/graph/builder/stages/parse-files.d.ts +3 -0
- package/dist/domain/graph/builder/stages/parse-files.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/parse-files.js +22 -0
- package/dist/domain/graph/builder/stages/parse-files.js.map +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts +7 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.js +231 -0
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -0
- package/dist/domain/graph/builder/stages/run-analyses.d.ts +3 -0
- package/dist/domain/graph/builder/stages/run-analyses.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/run-analyses.js +23 -0
- package/dist/domain/graph/builder/stages/run-analyses.js.map +1 -0
- package/dist/domain/graph/builder.d.ts +4 -0
- package/dist/domain/graph/builder.d.ts.map +1 -0
- package/dist/domain/graph/builder.js +6 -0
- package/dist/domain/graph/builder.js.map +1 -0
- package/dist/domain/graph/change-journal.d.ts +50 -0
- package/dist/domain/graph/change-journal.d.ts.map +1 -0
- package/dist/domain/graph/change-journal.js +97 -0
- package/dist/domain/graph/change-journal.js.map +1 -0
- package/dist/domain/graph/cycles.d.ts +19 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -0
- package/dist/domain/graph/cycles.js +142 -0
- package/dist/domain/graph/cycles.js.map +1 -0
- package/dist/domain/graph/journal.d.ts +30 -0
- package/dist/domain/graph/journal.d.ts.map +1 -0
- package/dist/domain/graph/journal.js +360 -0
- package/dist/domain/graph/journal.js.map +1 -0
- package/dist/domain/graph/resolve.d.ts +67 -0
- package/dist/domain/graph/resolve.d.ts.map +1 -0
- package/dist/domain/graph/resolve.js +521 -0
- package/dist/domain/graph/resolve.js.map +1 -0
- package/dist/domain/graph/resolver/points-to.d.ts +53 -0
- package/dist/domain/graph/resolver/points-to.d.ts.map +1 -0
- package/dist/domain/graph/resolver/points-to.js +261 -0
- package/dist/domain/graph/resolver/points-to.js.map +1 -0
- 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/resolver/ts-resolver.d.ts +9 -0
- package/dist/domain/graph/resolver/ts-resolver.d.ts.map +1 -0
- package/dist/domain/graph/resolver/ts-resolver.js +476 -0
- package/dist/domain/graph/resolver/ts-resolver.js.map +1 -0
- package/dist/domain/graph/watcher.d.ts +23 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -0
- package/dist/domain/graph/watcher.js +272 -0
- package/dist/domain/graph/watcher.js.map +1 -0
- package/dist/domain/parser.d.ts +128 -0
- package/dist/domain/parser.d.ts.map +1 -0
- package/dist/domain/parser.js +1246 -0
- package/dist/domain/parser.js.map +1 -0
- package/dist/domain/queries.d.ts +22 -0
- package/dist/domain/queries.d.ts.map +1 -0
- package/dist/domain/queries.js +26 -0
- package/dist/domain/queries.js.map +1 -0
- package/dist/domain/search/generator.d.ts +16 -0
- package/dist/domain/search/generator.d.ts.map +1 -0
- package/dist/domain/search/generator.js +181 -0
- package/dist/domain/search/generator.js.map +1 -0
- package/dist/domain/search/index.d.ts +15 -0
- package/dist/domain/search/index.d.ts.map +1 -0
- package/dist/domain/search/index.js +13 -0
- package/dist/domain/search/index.js.map +1 -0
- package/dist/domain/search/models.d.ts +63 -0
- package/dist/domain/search/models.d.ts.map +1 -0
- package/dist/domain/search/models.js +303 -0
- package/dist/domain/search/models.js.map +1 -0
- package/dist/domain/search/search/cli-formatter.d.ts +10 -0
- package/dist/domain/search/search/cli-formatter.d.ts.map +1 -0
- package/dist/domain/search/search/cli-formatter.js +134 -0
- package/dist/domain/search/search/cli-formatter.js.map +1 -0
- package/dist/domain/search/search/filters.d.ts +9 -0
- package/dist/domain/search/search/filters.d.ts.map +1 -0
- package/dist/domain/search/search/filters.js +31 -0
- package/dist/domain/search/search/filters.js.map +1 -0
- package/dist/domain/search/search/hybrid.d.ts +21 -0
- package/dist/domain/search/search/hybrid.d.ts.map +1 -0
- package/dist/domain/search/search/hybrid.js +122 -0
- package/dist/domain/search/search/hybrid.js.map +1 -0
- package/dist/domain/search/search/keyword.d.ts +18 -0
- package/dist/domain/search/search/keyword.d.ts.map +1 -0
- package/dist/domain/search/search/keyword.js +58 -0
- package/dist/domain/search/search/keyword.js.map +1 -0
- package/dist/domain/search/search/prepare.d.ts +25 -0
- package/dist/domain/search/search/prepare.d.ts.map +1 -0
- package/dist/domain/search/search/prepare.js +63 -0
- package/dist/domain/search/search/prepare.js.map +1 -0
- package/dist/domain/search/search/semantic.d.ts +41 -0
- package/dist/domain/search/search/semantic.d.ts.map +1 -0
- package/dist/domain/search/search/semantic.js +135 -0
- package/dist/domain/search/search/semantic.js.map +1 -0
- package/dist/domain/search/stores/fts5.d.ts +10 -0
- package/dist/domain/search/stores/fts5.d.ts.map +1 -0
- package/dist/domain/search/stores/fts5.js +27 -0
- package/dist/domain/search/stores/fts5.js.map +1 -0
- package/dist/domain/search/stores/sqlite-blob.d.ts +5 -0
- package/dist/domain/search/stores/sqlite-blob.d.ts.map +1 -0
- package/dist/domain/search/stores/sqlite-blob.js +16 -0
- package/dist/domain/search/stores/sqlite-blob.js.map +1 -0
- package/dist/domain/search/strategies/source.d.ts +6 -0
- package/dist/domain/search/strategies/source.d.ts.map +1 -0
- package/dist/domain/search/strategies/source.js +14 -0
- package/dist/domain/search/strategies/source.js.map +1 -0
- package/dist/domain/search/strategies/structured.d.ts +10 -0
- package/dist/domain/search/strategies/structured.d.ts.map +1 -0
- package/dist/domain/search/strategies/structured.js +35 -0
- package/dist/domain/search/strategies/structured.js.map +1 -0
- package/dist/domain/search/strategies/text-utils.d.ts +9 -0
- package/dist/domain/search/strategies/text-utils.d.ts.map +1 -0
- package/dist/domain/search/strategies/text-utils.js +45 -0
- package/dist/domain/search/strategies/text-utils.js.map +1 -0
- package/dist/domain/wasm-worker-entry.d.ts +24 -0
- package/dist/domain/wasm-worker-entry.d.ts.map +1 -0
- package/dist/domain/wasm-worker-entry.js +749 -0
- package/dist/domain/wasm-worker-entry.js.map +1 -0
- package/dist/domain/wasm-worker-pool.d.ts +59 -0
- package/dist/domain/wasm-worker-pool.d.ts.map +1 -0
- package/dist/domain/wasm-worker-pool.js +363 -0
- package/dist/domain/wasm-worker-pool.js.map +1 -0
- package/dist/domain/wasm-worker-protocol.d.ts +81 -0
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -0
- package/dist/domain/wasm-worker-protocol.js +13 -0
- package/dist/domain/wasm-worker-protocol.js.map +1 -0
- package/dist/extractors/bash.d.ts +6 -0
- package/dist/extractors/bash.d.ts.map +1 -0
- package/dist/extractors/bash.js +91 -0
- package/dist/extractors/bash.js.map +1 -0
- package/dist/extractors/c.d.ts +6 -0
- package/dist/extractors/c.d.ts.map +1 -0
- package/dist/extractors/c.js +223 -0
- package/dist/extractors/c.js.map +1 -0
- package/dist/extractors/clojure.d.ts +12 -0
- package/dist/extractors/clojure.d.ts.map +1 -0
- package/dist/extractors/clojure.js +245 -0
- package/dist/extractors/clojure.js.map +1 -0
- package/dist/extractors/cpp.d.ts +6 -0
- package/dist/extractors/cpp.d.ts.map +1 -0
- package/dist/extractors/cpp.js +365 -0
- package/dist/extractors/cpp.js.map +1 -0
- package/dist/extractors/csharp.d.ts +6 -0
- package/dist/extractors/csharp.d.ts.map +1 -0
- package/dist/extractors/csharp.js +430 -0
- package/dist/extractors/csharp.js.map +1 -0
- package/dist/extractors/cuda.d.ts +11 -0
- package/dist/extractors/cuda.d.ts.map +1 -0
- package/dist/extractors/cuda.js +419 -0
- package/dist/extractors/cuda.js.map +1 -0
- package/dist/extractors/dart.d.ts +6 -0
- package/dist/extractors/dart.d.ts.map +1 -0
- package/dist/extractors/dart.js +277 -0
- package/dist/extractors/dart.js.map +1 -0
- package/dist/extractors/elixir.d.ts +9 -0
- package/dist/extractors/elixir.d.ts.map +1 -0
- package/dist/extractors/elixir.js +327 -0
- package/dist/extractors/elixir.js.map +1 -0
- package/dist/extractors/erlang.d.ts +14 -0
- package/dist/extractors/erlang.d.ts.map +1 -0
- package/dist/extractors/erlang.js +275 -0
- package/dist/extractors/erlang.js.map +1 -0
- package/dist/extractors/fsharp.d.ts +20 -0
- package/dist/extractors/fsharp.d.ts.map +1 -0
- package/dist/extractors/fsharp.js +312 -0
- package/dist/extractors/fsharp.js.map +1 -0
- package/dist/extractors/gleam.d.ts +14 -0
- package/dist/extractors/gleam.d.ts.map +1 -0
- package/dist/extractors/gleam.js +225 -0
- package/dist/extractors/gleam.js.map +1 -0
- package/dist/extractors/go.d.ts +6 -0
- package/dist/extractors/go.d.ts.map +1 -0
- package/dist/extractors/go.js +460 -0
- package/dist/extractors/go.js.map +1 -0
- package/dist/extractors/groovy.d.ts +10 -0
- package/dist/extractors/groovy.d.ts.map +1 -0
- package/dist/extractors/groovy.js +403 -0
- package/dist/extractors/groovy.js.map +1 -0
- package/dist/extractors/haskell.d.ts +8 -0
- package/dist/extractors/haskell.d.ts.map +1 -0
- package/dist/extractors/haskell.js +261 -0
- package/dist/extractors/haskell.js.map +1 -0
- package/dist/extractors/hcl.d.ts +6 -0
- package/dist/extractors/hcl.d.ts.map +1 -0
- package/dist/extractors/hcl.js +114 -0
- package/dist/extractors/hcl.js.map +1 -0
- package/dist/extractors/helpers.d.ts +144 -0
- package/dist/extractors/helpers.d.ts.map +1 -0
- package/dist/extractors/helpers.js +360 -0
- package/dist/extractors/helpers.js.map +1 -0
- package/dist/extractors/index.d.ts +33 -0
- package/dist/extractors/index.d.ts.map +1 -0
- package/dist/extractors/index.js +33 -0
- package/dist/extractors/index.js.map +1 -0
- package/dist/extractors/java.d.ts +6 -0
- package/dist/extractors/java.d.ts.map +1 -0
- package/dist/extractors/java.js +390 -0
- package/dist/extractors/java.js.map +1 -0
- package/dist/extractors/javascript.d.ts +10 -0
- package/dist/extractors/javascript.d.ts.map +1 -0
- package/dist/extractors/javascript.js +3618 -0
- package/dist/extractors/javascript.js.map +1 -0
- package/dist/extractors/julia.d.ts +16 -0
- package/dist/extractors/julia.d.ts.map +1 -0
- package/dist/extractors/julia.js +411 -0
- package/dist/extractors/julia.js.map +1 -0
- package/dist/extractors/kotlin.d.ts +6 -0
- package/dist/extractors/kotlin.d.ts.map +1 -0
- package/dist/extractors/kotlin.js +340 -0
- package/dist/extractors/kotlin.js.map +1 -0
- package/dist/extractors/lua.d.ts +6 -0
- package/dist/extractors/lua.d.ts.map +1 -0
- package/dist/extractors/lua.js +162 -0
- package/dist/extractors/lua.js.map +1 -0
- package/dist/extractors/objc.d.ts +9 -0
- package/dist/extractors/objc.d.ts.map +1 -0
- package/dist/extractors/objc.js +543 -0
- package/dist/extractors/objc.js.map +1 -0
- package/dist/extractors/ocaml.d.ts +6 -0
- package/dist/extractors/ocaml.d.ts.map +1 -0
- package/dist/extractors/ocaml.js +310 -0
- package/dist/extractors/ocaml.js.map +1 -0
- package/dist/extractors/php.d.ts +6 -0
- package/dist/extractors/php.d.ts.map +1 -0
- package/dist/extractors/php.js +381 -0
- package/dist/extractors/php.js.map +1 -0
- package/dist/extractors/python.d.ts +6 -0
- package/dist/extractors/python.d.ts.map +1 -0
- package/dist/extractors/python.js +428 -0
- package/dist/extractors/python.js.map +1 -0
- package/dist/extractors/r.d.ts +13 -0
- package/dist/extractors/r.d.ts.map +1 -0
- package/dist/extractors/r.js +267 -0
- package/dist/extractors/r.js.map +1 -0
- package/dist/extractors/ruby.d.ts +6 -0
- package/dist/extractors/ruby.d.ts.map +1 -0
- package/dist/extractors/ruby.js +281 -0
- package/dist/extractors/ruby.js.map +1 -0
- package/dist/extractors/rust.d.ts +6 -0
- package/dist/extractors/rust.d.ts.map +1 -0
- package/dist/extractors/rust.js +354 -0
- package/dist/extractors/rust.js.map +1 -0
- package/dist/extractors/scala.d.ts +6 -0
- package/dist/extractors/scala.d.ts.map +1 -0
- package/dist/extractors/scala.js +315 -0
- package/dist/extractors/scala.js.map +1 -0
- package/dist/extractors/solidity.d.ts +9 -0
- package/dist/extractors/solidity.d.ts.map +1 -0
- package/dist/extractors/solidity.js +351 -0
- package/dist/extractors/solidity.js.map +1 -0
- package/dist/extractors/swift.d.ts +6 -0
- package/dist/extractors/swift.d.ts.map +1 -0
- package/dist/extractors/swift.js +326 -0
- package/dist/extractors/swift.js.map +1 -0
- package/dist/extractors/verilog.d.ts +9 -0
- package/dist/extractors/verilog.d.ts.map +1 -0
- package/dist/extractors/verilog.js +351 -0
- package/dist/extractors/verilog.js.map +1 -0
- package/dist/extractors/zig.d.ts +9 -0
- package/dist/extractors/zig.d.ts.map +1 -0
- package/dist/extractors/zig.js +274 -0
- package/dist/extractors/zig.js.map +1 -0
- package/dist/features/ast.d.ts +78 -0
- package/dist/features/ast.d.ts.map +1 -0
- package/dist/features/ast.js +228 -0
- package/dist/features/ast.js.map +1 -0
- package/dist/features/audit.d.ts +15 -0
- package/dist/features/audit.d.ts.map +1 -0
- package/dist/features/audit.js +275 -0
- package/dist/features/audit.js.map +1 -0
- package/dist/features/batch.d.ts +54 -0
- package/dist/features/batch.d.ts.map +1 -0
- package/dist/features/batch.js +97 -0
- package/dist/features/batch.js.map +1 -0
- package/dist/features/boundaries.d.ts +51 -0
- package/dist/features/boundaries.d.ts.map +1 -0
- package/dist/features/boundaries.js +248 -0
- package/dist/features/boundaries.js.map +1 -0
- package/dist/features/branch-compare.d.ts +71 -0
- package/dist/features/branch-compare.d.ts.map +1 -0
- package/dist/features/branch-compare.js +444 -0
- package/dist/features/branch-compare.js.map +1 -0
- package/dist/features/cfg.d.ts +79 -0
- package/dist/features/cfg.d.ts.map +1 -0
- package/dist/features/cfg.js +483 -0
- package/dist/features/cfg.js.map +1 -0
- package/dist/features/check.d.ts +84 -0
- package/dist/features/check.d.ts.map +1 -0
- package/dist/features/check.js +290 -0
- package/dist/features/check.js.map +1 -0
- package/dist/features/cochange.d.ts +68 -0
- package/dist/features/cochange.d.ts.map +1 -0
- package/dist/features/cochange.js +384 -0
- package/dist/features/cochange.js.map +1 -0
- package/dist/features/communities.d.ts +26 -0
- package/dist/features/communities.d.ts.map +1 -0
- package/dist/features/communities.js +149 -0
- package/dist/features/communities.js.map +1 -0
- 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 +268 -0
- package/dist/features/complexity-query.js.map +1 -0
- package/dist/features/complexity.d.ts +52 -0
- package/dist/features/complexity.d.ts.map +1 -0
- package/dist/features/complexity.js +490 -0
- package/dist/features/complexity.js.map +1 -0
- package/dist/features/dataflow.d.ts +129 -0
- package/dist/features/dataflow.d.ts.map +1 -0
- package/dist/features/dataflow.js +1079 -0
- package/dist/features/dataflow.js.map +1 -0
- package/dist/features/export.d.ts +35 -0
- package/dist/features/export.d.ts.map +1 -0
- package/dist/features/export.js +340 -0
- package/dist/features/export.js.map +1 -0
- package/dist/features/flow.d.ts +21 -0
- package/dist/features/flow.d.ts.map +1 -0
- package/dist/features/flow.js +195 -0
- package/dist/features/flow.js.map +1 -0
- package/dist/features/graph-enrichment.d.ts +44 -0
- package/dist/features/graph-enrichment.d.ts.map +1 -0
- package/dist/features/graph-enrichment.js +312 -0
- package/dist/features/graph-enrichment.js.map +1 -0
- package/dist/features/manifesto.d.ts +23 -0
- package/dist/features/manifesto.d.ts.map +1 -0
- package/dist/features/manifesto.js +382 -0
- package/dist/features/manifesto.js.map +1 -0
- package/dist/features/owners.d.ts +60 -0
- package/dist/features/owners.d.ts.map +1 -0
- package/dist/features/owners.js +248 -0
- package/dist/features/owners.js.map +1 -0
- package/dist/features/sequence.d.ts +42 -0
- package/dist/features/sequence.d.ts.map +1 -0
- package/dist/features/sequence.js +271 -0
- package/dist/features/sequence.js.map +1 -0
- package/dist/features/shared/find-nodes.d.ts +7 -0
- package/dist/features/shared/find-nodes.d.ts.map +1 -0
- package/dist/features/shared/find-nodes.js +18 -0
- package/dist/features/shared/find-nodes.js.map +1 -0
- package/dist/features/snapshot.d.ts +25 -0
- package/dist/features/snapshot.d.ts.map +1 -0
- package/dist/features/snapshot.js +188 -0
- package/dist/features/snapshot.js.map +1 -0
- 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 +274 -0
- package/dist/features/structure-query.js.map +1 -0
- package/dist/features/structure.d.ts +39 -0
- package/dist/features/structure.d.ts.map +1 -0
- package/dist/features/structure.js +796 -0
- package/dist/features/structure.js.map +1 -0
- package/dist/features/triage.d.ts +47 -0
- package/dist/features/triage.d.ts.map +1 -0
- package/dist/features/triage.js +123 -0
- package/dist/features/triage.js.map +1 -0
- package/dist/graph/algorithms/bfs.d.ts +14 -0
- package/dist/graph/algorithms/bfs.d.ts.map +1 -0
- package/dist/graph/algorithms/bfs.js +72 -0
- package/dist/graph/algorithms/bfs.js.map +1 -0
- package/dist/graph/algorithms/centrality.d.ts +12 -0
- package/dist/graph/algorithms/centrality.d.ts.map +1 -0
- package/dist/graph/algorithms/centrality.js +42 -0
- package/dist/graph/algorithms/centrality.js.map +1 -0
- package/dist/graph/algorithms/index.d.ts +7 -0
- package/dist/graph/algorithms/index.d.ts.map +1 -0
- package/dist/graph/algorithms/index.js +7 -0
- package/dist/graph/algorithms/index.js.map +1 -0
- package/dist/graph/algorithms/leiden/adapter.d.ts +37 -0
- package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/adapter.js +173 -0
- package/dist/graph/algorithms/leiden/adapter.js.map +1 -0
- package/dist/graph/algorithms/leiden/cpm.d.ts +28 -0
- package/dist/graph/algorithms/leiden/cpm.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/cpm.js +48 -0
- package/dist/graph/algorithms/leiden/cpm.js.map +1 -0
- package/dist/graph/algorithms/leiden/index.d.ts +37 -0
- package/dist/graph/algorithms/leiden/index.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/index.js +157 -0
- package/dist/graph/algorithms/leiden/index.js.map +1 -0
- package/dist/graph/algorithms/leiden/modularity.d.ts +33 -0
- package/dist/graph/algorithms/leiden/modularity.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/modularity.js +80 -0
- package/dist/graph/algorithms/leiden/modularity.js.map +1 -0
- package/dist/graph/algorithms/leiden/optimiser.d.ts +62 -0
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/optimiser.js +530 -0
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -0
- package/dist/graph/algorithms/leiden/partition.d.ts +44 -0
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/partition.js +438 -0
- package/dist/graph/algorithms/leiden/partition.js.map +1 -0
- package/dist/graph/algorithms/leiden/rng.d.ts +9 -0
- package/dist/graph/algorithms/leiden/rng.d.ts.map +1 -0
- package/dist/graph/algorithms/leiden/rng.js +17 -0
- package/dist/graph/algorithms/leiden/rng.js.map +1 -0
- package/dist/graph/algorithms/louvain.d.ts +20 -0
- package/dist/graph/algorithms/louvain.d.ts.map +1 -0
- package/dist/graph/algorithms/louvain.js +52 -0
- package/dist/graph/algorithms/louvain.js.map +1 -0
- package/dist/graph/algorithms/shortest-path.d.ts +10 -0
- package/dist/graph/algorithms/shortest-path.d.ts.map +1 -0
- package/dist/graph/algorithms/shortest-path.js +55 -0
- package/dist/graph/algorithms/shortest-path.js.map +1 -0
- package/dist/graph/algorithms/tarjan.d.ts +9 -0
- package/dist/graph/algorithms/tarjan.d.ts.map +1 -0
- package/dist/graph/algorithms/tarjan.js +52 -0
- package/dist/graph/algorithms/tarjan.js.map +1 -0
- package/dist/graph/builders/dependency.d.ts +18 -0
- package/dist/graph/builders/dependency.d.ts.map +1 -0
- package/dist/graph/builders/dependency.js +101 -0
- package/dist/graph/builders/dependency.js.map +1 -0
- package/dist/graph/builders/index.d.ts +4 -0
- package/dist/graph/builders/index.d.ts.map +1 -0
- package/dist/graph/builders/index.js +4 -0
- package/dist/graph/builders/index.js.map +1 -0
- package/dist/graph/builders/structure.d.ts +10 -0
- package/dist/graph/builders/structure.d.ts.map +1 -0
- package/dist/graph/builders/structure.js +33 -0
- package/dist/graph/builders/structure.js.map +1 -0
- package/dist/graph/builders/temporal.d.ts +13 -0
- package/dist/graph/builders/temporal.d.ts.map +1 -0
- package/dist/graph/builders/temporal.js +29 -0
- package/dist/graph/builders/temporal.js.map +1 -0
- package/dist/graph/classifiers/index.d.ts +3 -0
- package/dist/graph/classifiers/index.d.ts.map +1 -0
- package/dist/graph/classifiers/index.js +3 -0
- package/dist/graph/classifiers/index.js.map +1 -0
- package/dist/graph/classifiers/risk.d.ts +39 -0
- package/dist/graph/classifiers/risk.d.ts.map +1 -0
- package/dist/graph/classifiers/risk.js +78 -0
- package/dist/graph/classifiers/risk.js.map +1 -0
- package/dist/graph/classifiers/roles.d.ts +48 -0
- package/dist/graph/classifiers/roles.d.ts.map +1 -0
- package/dist/graph/classifiers/roles.js +180 -0
- package/dist/graph/classifiers/roles.js.map +1 -0
- package/dist/graph/index.d.ts +5 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/model.d.ts +57 -0
- package/dist/graph/model.d.ts.map +1 -0
- package/dist/graph/model.js +205 -0
- package/dist/graph/model.js.map +1 -0
- package/dist/index.cjs +16 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/config.d.ts +281 -0
- package/dist/infrastructure/config.d.ts.map +1 -0
- package/dist/infrastructure/config.js +857 -0
- package/dist/infrastructure/config.js.map +1 -0
- package/dist/infrastructure/logger.d.ts +7 -0
- package/dist/infrastructure/logger.d.ts.map +1 -0
- package/dist/infrastructure/logger.js +21 -0
- package/dist/infrastructure/logger.js.map +1 -0
- package/dist/infrastructure/native.d.ts +38 -0
- package/dist/infrastructure/native.d.ts.map +1 -0
- package/dist/infrastructure/native.js +182 -0
- package/dist/infrastructure/native.js.map +1 -0
- package/dist/infrastructure/registry.d.ts +81 -0
- package/dist/infrastructure/registry.d.ts.map +1 -0
- package/dist/infrastructure/registry.js +229 -0
- package/dist/infrastructure/registry.js.map +1 -0
- package/dist/infrastructure/result-formatter.d.ts +2 -0
- package/dist/infrastructure/result-formatter.d.ts.map +1 -0
- package/dist/infrastructure/result-formatter.js +3 -0
- package/dist/infrastructure/result-formatter.js.map +1 -0
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/infrastructure/test-filter.d.ts +5 -0
- package/dist/infrastructure/test-filter.d.ts.map +1 -0
- package/dist/infrastructure/test-filter.js +7 -0
- package/dist/infrastructure/test-filter.js.map +1 -0
- package/dist/infrastructure/update-check.d.ts +26 -0
- package/dist/infrastructure/update-check.d.ts.map +1 -0
- package/dist/infrastructure/update-check.js +164 -0
- package/dist/infrastructure/update-check.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/middleware.d.ts +26 -0
- package/dist/mcp/middleware.d.ts.map +1 -0
- package/dist/mcp/middleware.js +32 -0
- package/dist/mcp/middleware.js.map +1 -0
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +193 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool-registry.d.ts +21 -0
- package/dist/mcp/tool-registry.d.ts.map +1 -0
- package/dist/mcp/tool-registry.js +839 -0
- package/dist/mcp/tool-registry.js.map +1 -0
- package/dist/mcp/tools/ast-query.d.ts +14 -0
- package/dist/mcp/tools/ast-query.d.ts.map +1 -0
- package/dist/mcp/tools/ast-query.js +13 -0
- package/dist/mcp/tools/ast-query.js.map +1 -0
- package/dist/mcp/tools/audit.d.ts +15 -0
- package/dist/mcp/tools/audit.d.ts.map +1 -0
- package/dist/mcp/tools/audit.js +20 -0
- package/dist/mcp/tools/audit.js.map +1 -0
- package/dist/mcp/tools/batch-query.d.ts +13 -0
- package/dist/mcp/tools/batch-query.d.ts.map +1 -0
- package/dist/mcp/tools/batch-query.js +11 -0
- package/dist/mcp/tools/batch-query.js.map +1 -0
- package/dist/mcp/tools/branch-compare.d.ts +12 -0
- package/dist/mcp/tools/branch-compare.d.ts.map +1 -0
- package/dist/mcp/tools/branch-compare.js +10 -0
- package/dist/mcp/tools/branch-compare.js.map +1 -0
- package/dist/mcp/tools/brief.d.ts +9 -0
- package/dist/mcp/tools/brief.d.ts.map +1 -0
- package/dist/mcp/tools/brief.js +8 -0
- package/dist/mcp/tools/brief.js.map +1 -0
- package/dist/mcp/tools/cfg.d.ts +14 -0
- package/dist/mcp/tools/cfg.d.ts.map +1 -0
- package/dist/mcp/tools/cfg.js +20 -0
- package/dist/mcp/tools/cfg.js.map +1 -0
- package/dist/mcp/tools/check.d.ts +20 -0
- package/dist/mcp/tools/check.d.ts.map +1 -0
- package/dist/mcp/tools/check.js +39 -0
- package/dist/mcp/tools/check.js.map +1 -0
- package/dist/mcp/tools/co-changes.d.ts +12 -0
- package/dist/mcp/tools/co-changes.d.ts.map +1 -0
- package/dist/mcp/tools/co-changes.js +19 -0
- package/dist/mcp/tools/co-changes.js.map +1 -0
- package/dist/mcp/tools/code-owners.d.ts +12 -0
- package/dist/mcp/tools/code-owners.d.ts.map +1 -0
- package/dist/mcp/tools/code-owners.js +12 -0
- package/dist/mcp/tools/code-owners.js.map +1 -0
- package/dist/mcp/tools/communities.d.ts +13 -0
- package/dist/mcp/tools/communities.d.ts.map +1 -0
- package/dist/mcp/tools/communities.js +14 -0
- package/dist/mcp/tools/communities.js.map +1 -0
- package/dist/mcp/tools/complexity.d.ts +16 -0
- package/dist/mcp/tools/complexity.d.ts.map +1 -0
- package/dist/mcp/tools/complexity.js +16 -0
- package/dist/mcp/tools/complexity.js.map +1 -0
- package/dist/mcp/tools/context.d.ts +16 -0
- package/dist/mcp/tools/context.d.ts.map +1 -0
- package/dist/mcp/tools/context.js +16 -0
- package/dist/mcp/tools/context.js.map +1 -0
- package/dist/mcp/tools/dataflow.d.ts +15 -0
- package/dist/mcp/tools/dataflow.d.ts.map +1 -0
- package/dist/mcp/tools/dataflow.js +25 -0
- package/dist/mcp/tools/dataflow.js.map +1 -0
- package/dist/mcp/tools/diff-impact.d.ts +14 -0
- package/dist/mcp/tools/diff-impact.d.ts.map +1 -0
- package/dist/mcp/tools/diff-impact.js +23 -0
- package/dist/mcp/tools/diff-impact.js.map +1 -0
- package/dist/mcp/tools/execution-flow.d.ts +15 -0
- package/dist/mcp/tools/execution-flow.d.ts.map +1 -0
- package/dist/mcp/tools/execution-flow.js +25 -0
- package/dist/mcp/tools/execution-flow.js.map +1 -0
- package/dist/mcp/tools/export-graph.d.ts +11 -0
- package/dist/mcp/tools/export-graph.d.ts.map +1 -0
- package/dist/mcp/tools/export-graph.js +57 -0
- package/dist/mcp/tools/export-graph.js.map +1 -0
- package/dist/mcp/tools/file-deps.d.ts +11 -0
- package/dist/mcp/tools/file-deps.d.ts.map +1 -0
- package/dist/mcp/tools/file-deps.js +11 -0
- package/dist/mcp/tools/file-deps.js.map +1 -0
- package/dist/mcp/tools/file-exports.d.ts +12 -0
- package/dist/mcp/tools/file-exports.d.ts.map +1 -0
- package/dist/mcp/tools/file-exports.js +12 -0
- package/dist/mcp/tools/file-exports.js.map +1 -0
- package/dist/mcp/tools/find-cycles.d.ts +7 -0
- package/dist/mcp/tools/find-cycles.d.ts.map +1 -0
- package/dist/mcp/tools/find-cycles.js +15 -0
- package/dist/mcp/tools/find-cycles.js.map +1 -0
- package/dist/mcp/tools/fn-impact.d.ts +14 -0
- package/dist/mcp/tools/fn-impact.d.ts.map +1 -0
- package/dist/mcp/tools/fn-impact.js +14 -0
- package/dist/mcp/tools/fn-impact.js.map +1 -0
- package/dist/mcp/tools/impact-analysis.d.ts +9 -0
- package/dist/mcp/tools/impact-analysis.d.ts.map +1 -0
- package/dist/mcp/tools/impact-analysis.js +8 -0
- package/dist/mcp/tools/impact-analysis.js.map +1 -0
- package/dist/mcp/tools/implementations.d.ts +13 -0
- package/dist/mcp/tools/implementations.d.ts.map +1 -0
- package/dist/mcp/tools/implementations.js +13 -0
- package/dist/mcp/tools/implementations.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +7 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +76 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/interfaces.d.ts +13 -0
- package/dist/mcp/tools/interfaces.d.ts.map +1 -0
- package/dist/mcp/tools/interfaces.js +13 -0
- package/dist/mcp/tools/interfaces.js.map +1 -0
- package/dist/mcp/tools/list-functions.d.ts +12 -0
- package/dist/mcp/tools/list-functions.d.ts.map +1 -0
- package/dist/mcp/tools/list-functions.js +13 -0
- package/dist/mcp/tools/list-functions.js.map +1 -0
- package/dist/mcp/tools/list-repos.d.ts +8 -0
- package/dist/mcp/tools/list-repos.d.ts.map +1 -0
- package/dist/mcp/tools/list-repos.js +11 -0
- package/dist/mcp/tools/list-repos.js.map +1 -0
- package/dist/mcp/tools/module-map.d.ts +9 -0
- package/dist/mcp/tools/module-map.d.ts.map +1 -0
- package/dist/mcp/tools/module-map.js +6 -0
- package/dist/mcp/tools/module-map.js.map +1 -0
- package/dist/mcp/tools/node-roles.d.ts +12 -0
- package/dist/mcp/tools/node-roles.d.ts.map +1 -0
- package/dist/mcp/tools/node-roles.js +13 -0
- package/dist/mcp/tools/node-roles.js.map +1 -0
- package/dist/mcp/tools/path.d.ts +15 -0
- package/dist/mcp/tools/path.d.ts.map +1 -0
- package/dist/mcp/tools/path.js +21 -0
- package/dist/mcp/tools/path.js.map +1 -0
- package/dist/mcp/tools/query.d.ts +20 -0
- package/dist/mcp/tools/query.d.ts.map +1 -0
- package/dist/mcp/tools/query.js +29 -0
- package/dist/mcp/tools/query.js.map +1 -0
- package/dist/mcp/tools/semantic-search.d.ts +13 -0
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -0
- package/dist/mcp/tools/semantic-search.js +62 -0
- package/dist/mcp/tools/semantic-search.js.map +1 -0
- package/dist/mcp/tools/sequence.d.ts +16 -0
- package/dist/mcp/tools/sequence.d.ts.map +1 -0
- package/dist/mcp/tools/sequence.js +16 -0
- package/dist/mcp/tools/sequence.js.map +1 -0
- package/dist/mcp/tools/structure.d.ts +13 -0
- package/dist/mcp/tools/structure.d.ts.map +1 -0
- package/dist/mcp/tools/structure.js +14 -0
- package/dist/mcp/tools/structure.js.map +1 -0
- package/dist/mcp/tools/symbol-children.d.ts +13 -0
- package/dist/mcp/tools/symbol-children.d.ts.map +1 -0
- package/dist/mcp/tools/symbol-children.js +13 -0
- package/dist/mcp/tools/symbol-children.js.map +1 -0
- package/dist/mcp/tools/triage.d.ts +17 -0
- package/dist/mcp/tools/triage.d.ts.map +1 -0
- package/dist/mcp/tools/triage.js +34 -0
- package/dist/mcp/tools/triage.js.map +1 -0
- package/dist/mcp/tools/where.d.ts +12 -0
- package/dist/mcp/tools/where.d.ts.map +1 -0
- package/dist/mcp/tools/where.js +12 -0
- package/dist/mcp/tools/where.js.map +1 -0
- 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/audit.d.ts +15 -0
- package/dist/presentation/audit.d.ts.map +1 -0
- package/dist/presentation/audit.js +78 -0
- package/dist/presentation/audit.js.map +1 -0
- package/dist/presentation/batch.d.ts +20 -0
- package/dist/presentation/batch.d.ts.map +1 -0
- package/dist/presentation/batch.js +21 -0
- package/dist/presentation/batch.js.map +1 -0
- package/dist/presentation/branch-compare.d.ts +13 -0
- package/dist/presentation/branch-compare.d.ts.map +1 -0
- package/dist/presentation/branch-compare.js +91 -0
- package/dist/presentation/branch-compare.js.map +1 -0
- package/dist/presentation/brief.d.ts +9 -0
- package/dist/presentation/brief.d.ts.map +1 -0
- package/dist/presentation/brief.js +39 -0
- package/dist/presentation/brief.js.map +1 -0
- package/dist/presentation/cfg.d.ts +13 -0
- package/dist/presentation/cfg.d.ts.map +1 -0
- package/dist/presentation/cfg.js +62 -0
- package/dist/presentation/cfg.js.map +1 -0
- package/dist/presentation/check.d.ts +19 -0
- package/dist/presentation/check.d.ts.map +1 -0
- package/dist/presentation/check.js +74 -0
- package/dist/presentation/check.js.map +1 -0
- package/dist/presentation/cochange.d.ts +32 -0
- package/dist/presentation/cochange.d.ts.map +1 -0
- package/dist/presentation/cochange.js +33 -0
- package/dist/presentation/cochange.js.map +1 -0
- package/dist/presentation/colors.d.ts +12 -0
- package/dist/presentation/colors.d.ts.map +1 -0
- package/dist/presentation/colors.js +47 -0
- package/dist/presentation/colors.js.map +1 -0
- package/dist/presentation/communities.d.ts +11 -0
- package/dist/presentation/communities.d.ts.map +1 -0
- package/dist/presentation/communities.js +57 -0
- package/dist/presentation/communities.js.map +1 -0
- package/dist/presentation/complexity.d.ts +17 -0
- package/dist/presentation/complexity.d.ts.map +1 -0
- package/dist/presentation/complexity.js +59 -0
- package/dist/presentation/complexity.js.map +1 -0
- package/dist/presentation/dataflow.d.ts +14 -0
- package/dist/presentation/dataflow.d.ts.map +1 -0
- package/dist/presentation/dataflow.js +100 -0
- package/dist/presentation/dataflow.js.map +1 -0
- 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 +114 -0
- package/dist/presentation/diff-impact-mermaid.js.map +1 -0
- package/dist/presentation/export.d.ts +130 -0
- package/dist/presentation/export.d.ts.map +1 -0
- package/dist/presentation/export.js +311 -0
- package/dist/presentation/export.js.map +1 -0
- package/dist/presentation/flow.d.ts +16 -0
- package/dist/presentation/flow.d.ts.map +1 -0
- package/dist/presentation/flow.js +85 -0
- package/dist/presentation/flow.js.map +1 -0
- package/dist/presentation/manifesto.d.ts +13 -0
- package/dist/presentation/manifesto.d.ts.map +1 -0
- package/dist/presentation/manifesto.js +51 -0
- package/dist/presentation/manifesto.js.map +1 -0
- package/dist/presentation/owners.d.ts +16 -0
- package/dist/presentation/owners.d.ts.map +1 -0
- package/dist/presentation/owners.js +42 -0
- package/dist/presentation/owners.js.map +1 -0
- package/dist/presentation/queries-cli/exports.d.ts +12 -0
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -0
- package/dist/presentation/queries-cli/exports.js +100 -0
- package/dist/presentation/queries-cli/exports.js.map +1 -0
- package/dist/presentation/queries-cli/impact.d.ts +23 -0
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -0
- package/dist/presentation/queries-cli/impact.js +208 -0
- package/dist/presentation/queries-cli/impact.js.map +1 -0
- package/dist/presentation/queries-cli/index.d.ts +6 -0
- package/dist/presentation/queries-cli/index.d.ts.map +1 -0
- package/dist/presentation/queries-cli/index.js +6 -0
- package/dist/presentation/queries-cli/index.js.map +1 -0
- package/dist/presentation/queries-cli/inspect.d.ts +23 -0
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -0
- package/dist/presentation/queries-cli/inspect.js +376 -0
- package/dist/presentation/queries-cli/inspect.js.map +1 -0
- package/dist/presentation/queries-cli/overview.d.ts +19 -0
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -0
- package/dist/presentation/queries-cli/overview.js +196 -0
- package/dist/presentation/queries-cli/overview.js.map +1 -0
- package/dist/presentation/queries-cli/path.d.ts +15 -0
- package/dist/presentation/queries-cli/path.d.ts.map +1 -0
- package/dist/presentation/queries-cli/path.js +101 -0
- package/dist/presentation/queries-cli/path.js.map +1 -0
- package/dist/presentation/queries-cli.d.ts +12 -0
- package/dist/presentation/queries-cli.d.ts.map +1 -0
- package/dist/presentation/queries-cli.js +12 -0
- package/dist/presentation/queries-cli.js.map +1 -0
- package/dist/presentation/query.d.ts +2 -0
- package/dist/presentation/query.d.ts.map +1 -0
- package/dist/presentation/query.js +2 -0
- package/dist/presentation/query.js.map +1 -0
- package/dist/presentation/result-formatter.d.ts +23 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -0
- package/dist/presentation/result-formatter.js +122 -0
- package/dist/presentation/result-formatter.js.map +1 -0
- package/dist/presentation/sequence-renderer.d.ts +19 -0
- package/dist/presentation/sequence-renderer.d.ts.map +1 -0
- package/dist/presentation/sequence-renderer.js +23 -0
- package/dist/presentation/sequence-renderer.js.map +1 -0
- package/dist/presentation/sequence.d.ts +15 -0
- package/dist/presentation/sequence.d.ts.map +1 -0
- package/dist/presentation/sequence.js +26 -0
- package/dist/presentation/sequence.js.map +1 -0
- package/dist/presentation/structure.d.ts +46 -0
- package/dist/presentation/structure.d.ts.map +1 -0
- package/dist/presentation/structure.js +50 -0
- package/dist/presentation/structure.js.map +1 -0
- package/dist/presentation/table.d.ts +24 -0
- package/dist/presentation/table.d.ts.map +1 -0
- package/dist/presentation/table.js +33 -0
- package/dist/presentation/table.js.map +1 -0
- package/dist/presentation/triage.d.ts +19 -0
- package/dist/presentation/triage.d.ts.map +1 -0
- package/dist/presentation/triage.js +34 -0
- package/dist/presentation/triage.js.map +1 -0
- package/dist/presentation/viewer.d.ts +102 -0
- package/dist/presentation/viewer.d.ts.map +1 -0
- package/dist/presentation/viewer.js +618 -0
- package/dist/presentation/viewer.js.map +1 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +54 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/errors.d.ts +46 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +69 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/file-utils.d.ts +27 -0
- package/dist/shared/file-utils.d.ts.map +1 -0
- package/dist/shared/file-utils.js +186 -0
- package/dist/shared/file-utils.js.map +1 -0
- package/dist/shared/generators.d.ts +57 -0
- package/dist/shared/generators.d.ts.map +1 -0
- package/dist/shared/generators.js +106 -0
- package/dist/shared/generators.js.map +1 -0
- package/dist/shared/globs.d.ts +40 -0
- package/dist/shared/globs.d.ts.map +1 -0
- package/dist/shared/globs.js +126 -0
- package/dist/shared/globs.js.map +1 -0
- package/dist/shared/hierarchy.d.ts +9 -0
- package/dist/shared/hierarchy.d.ts.map +1 -0
- package/dist/shared/hierarchy.js +68 -0
- package/dist/shared/hierarchy.js.map +1 -0
- package/dist/shared/kinds.d.ts +12 -0
- package/dist/shared/kinds.d.ts.map +1 -0
- package/dist/shared/kinds.js +67 -0
- package/dist/shared/kinds.js.map +1 -0
- package/dist/shared/normalize.d.ts +49 -0
- package/dist/shared/normalize.d.ts.map +1 -0
- package/dist/shared/normalize.js +57 -0
- package/dist/shared/normalize.js.map +1 -0
- package/dist/shared/paginate.d.ts +32 -0
- package/dist/shared/paginate.d.ts.map +1 -0
- package/dist/shared/paginate.js +82 -0
- package/dist/shared/paginate.js.map +1 -0
- 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 +2468 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-clojure.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-cuda.wasm +0 -0
- package/grammars/tree-sitter-dart.wasm +0 -0
- package/grammars/tree-sitter-elixir.wasm +0 -0
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-fsharp.wasm +0 -0
- package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-groovy.wasm +0 -0
- package/grammars/tree-sitter-haskell.wasm +0 -0
- package/grammars/tree-sitter-hcl.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-julia.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-lua.wasm +0 -0
- package/grammars/tree-sitter-objc.wasm +0 -0
- package/grammars/tree-sitter-ocaml.wasm +0 -0
- package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-r.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-solidity.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/grammars/tree-sitter-verilog.wasm +0 -0
- package/grammars/tree-sitter-zig.wasm +0 -0
- package/package.json +188 -0
- package/src/ast-analysis/engine.ts +926 -0
- package/src/ast-analysis/metrics.ts +150 -0
- 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/csharp.ts +210 -0
- package/src/ast-analysis/rules/go.ts +187 -0
- package/src/ast-analysis/rules/index.ts +380 -0
- package/src/ast-analysis/rules/java.ts +181 -0
- package/src/ast-analysis/rules/javascript.ts +249 -0
- package/src/ast-analysis/rules/php.ts +226 -0
- package/src/ast-analysis/rules/python.ts +202 -0
- package/src/ast-analysis/rules/ruby.ts +209 -0
- package/src/ast-analysis/rules/rust.ts +179 -0
- package/src/ast-analysis/shared.ts +223 -0
- package/src/ast-analysis/visitor-utils.ts +278 -0
- package/src/ast-analysis/visitor.ts +266 -0
- package/src/ast-analysis/visitors/ast-store-visitor.ts +334 -0
- 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 +367 -0
- package/src/ast-analysis/visitors/complexity-visitor.ts +328 -0
- package/src/ast-analysis/visitors/dataflow-visitor.ts +571 -0
- package/src/cli/commands/ast.ts +24 -0
- package/src/cli/commands/audit.ts +48 -0
- package/src/cli/commands/batch.ts +66 -0
- package/src/cli/commands/branch-compare.ts +27 -0
- package/src/cli/commands/brief.ts +13 -0
- package/src/cli/commands/build.ts +35 -0
- package/src/cli/commands/cfg.ts +28 -0
- package/src/cli/commands/check.ts +72 -0
- package/src/cli/commands/children.ts +34 -0
- package/src/cli/commands/co-change.ts +72 -0
- package/src/cli/commands/communities.ts +21 -0
- package/src/cli/commands/complexity.ts +48 -0
- package/src/cli/commands/config.ts +353 -0
- package/src/cli/commands/context.ts +36 -0
- package/src/cli/commands/cycles.ts +33 -0
- package/src/cli/commands/dataflow.ts +30 -0
- package/src/cli/commands/deps.ts +18 -0
- package/src/cli/commands/diff-impact.ts +30 -0
- package/src/cli/commands/embed.ts +82 -0
- package/src/cli/commands/export.ts +79 -0
- package/src/cli/commands/exports.ts +15 -0
- package/src/cli/commands/flow.ts +34 -0
- package/src/cli/commands/fn-impact.ts +34 -0
- package/src/cli/commands/impact.ts +13 -0
- package/src/cli/commands/implementations.ts +30 -0
- package/src/cli/commands/info.ts +79 -0
- package/src/cli/commands/interfaces.ts +30 -0
- package/src/cli/commands/map.ts +20 -0
- package/src/cli/commands/mcp.ts +19 -0
- package/src/cli/commands/models.ts +29 -0
- package/src/cli/commands/owners.ts +28 -0
- package/src/cli/commands/path.ts +42 -0
- package/src/cli/commands/plot.ts +110 -0
- package/src/cli/commands/query.ts +51 -0
- package/src/cli/commands/registry.ts +106 -0
- package/src/cli/commands/roles.ts +38 -0
- package/src/cli/commands/search.ts +45 -0
- package/src/cli/commands/sequence.ts +30 -0
- package/src/cli/commands/snapshot.ts +67 -0
- package/src/cli/commands/stats.ts +16 -0
- package/src/cli/commands/structure.ts +55 -0
- package/src/cli/commands/triage.ts +93 -0
- package/src/cli/commands/watch.ts +29 -0
- package/src/cli/commands/where.ts +21 -0
- package/src/cli/index.ts +159 -0
- package/src/cli/shared/open-graph.ts +13 -0
- package/src/cli/shared/options.ts +69 -0
- package/src/cli/shared/output.ts +1 -0
- package/src/cli/types.ts +35 -0
- package/src/cli.ts +37 -0
- package/src/db/better-sqlite3.ts +21 -0
- package/src/db/connection.ts +478 -0
- package/src/db/index.ts +84 -0
- package/src/db/migrations.ts +440 -0
- package/src/db/query-builder.ts +371 -0
- package/src/db/repository/base.ts +269 -0
- package/src/db/repository/build-stmts.ts +126 -0
- package/src/db/repository/cached-stmt.ts +20 -0
- package/src/db/repository/cfg.ts +81 -0
- package/src/db/repository/cochange.ts +48 -0
- package/src/db/repository/complexity.ts +21 -0
- package/src/db/repository/dataflow.ts +35 -0
- package/src/db/repository/edges.ts +281 -0
- package/src/db/repository/embeddings.ts +47 -0
- package/src/db/repository/graph-read.ts +62 -0
- package/src/db/repository/in-memory-repository.ts +657 -0
- package/src/db/repository/index.ts +53 -0
- package/src/db/repository/native-repository.ts +546 -0
- package/src/db/repository/nodes.ts +322 -0
- package/src/db/repository/sqlite-repository.ts +303 -0
- package/src/domain/analysis/brief.ts +165 -0
- package/src/domain/analysis/context.ts +537 -0
- package/src/domain/analysis/dependencies.ts +738 -0
- package/src/domain/analysis/diff-impact.ts +381 -0
- package/src/domain/analysis/exports.ts +262 -0
- package/src/domain/analysis/fn-impact.ts +311 -0
- package/src/domain/analysis/impact.ts +11 -0
- package/src/domain/analysis/implementations.ts +81 -0
- package/src/domain/analysis/module-map.ts +576 -0
- package/src/domain/analysis/query-helpers.ts +52 -0
- package/src/domain/analysis/roles.ts +95 -0
- package/src/domain/analysis/symbol-lookup.ts +284 -0
- package/src/domain/graph/builder/call-resolver.ts +289 -0
- package/src/domain/graph/builder/cha.ts +192 -0
- package/src/domain/graph/builder/context.ts +120 -0
- package/src/domain/graph/builder/helpers.ts +665 -0
- package/src/domain/graph/builder/incremental.ts +987 -0
- package/src/domain/graph/builder/native-db-proxy.ts +104 -0
- package/src/domain/graph/builder/pipeline.ts +517 -0
- package/src/domain/graph/builder/stages/build-edges.ts +1986 -0
- package/src/domain/graph/builder/stages/build-structure.ts +330 -0
- package/src/domain/graph/builder/stages/collect-files.ts +175 -0
- package/src/domain/graph/builder/stages/detect-changes.ts +751 -0
- package/src/domain/graph/builder/stages/finalize.ts +321 -0
- package/src/domain/graph/builder/stages/insert-nodes.ts +489 -0
- package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +2241 -0
- package/src/domain/graph/builder/stages/parse-files.ts +26 -0
- package/src/domain/graph/builder/stages/resolve-imports.ts +285 -0
- package/src/domain/graph/builder/stages/run-analyses.ts +26 -0
- package/src/domain/graph/builder.ts +11 -0
- package/src/domain/graph/change-journal.ts +141 -0
- package/src/domain/graph/cycles.ts +155 -0
- package/src/domain/graph/journal.ts +377 -0
- package/src/domain/graph/resolve.ts +579 -0
- package/src/domain/graph/resolver/points-to.ts +377 -0
- package/src/domain/graph/resolver/strategy.ts +265 -0
- package/src/domain/graph/resolver/ts-resolver.ts +536 -0
- package/src/domain/graph/watcher.ts +339 -0
- package/src/domain/parser.ts +1437 -0
- package/src/domain/queries.ts +50 -0
- package/src/domain/search/generator.ts +260 -0
- package/src/domain/search/index.ts +15 -0
- package/src/domain/search/models.ts +356 -0
- package/src/domain/search/search/cli-formatter.ts +179 -0
- package/src/domain/search/search/filters.ts +38 -0
- package/src/domain/search/search/hybrid.ts +197 -0
- package/src/domain/search/search/keyword.ts +101 -0
- package/src/domain/search/search/prepare.ts +95 -0
- package/src/domain/search/search/semantic.ts +226 -0
- package/src/domain/search/stores/fts5.ts +27 -0
- package/src/domain/search/stores/sqlite-blob.ts +15 -0
- package/src/domain/search/strategies/source.ts +19 -0
- package/src/domain/search/strategies/structured.ts +48 -0
- package/src/domain/search/strategies/text-utils.ts +41 -0
- package/src/domain/wasm-worker-entry.ts +921 -0
- package/src/domain/wasm-worker-pool.ts +375 -0
- package/src/domain/wasm-worker-protocol.ts +98 -0
- package/src/extractors/bash.ts +97 -0
- package/src/extractors/c.ts +231 -0
- package/src/extractors/clojure.ts +273 -0
- package/src/extractors/cpp.ts +383 -0
- package/src/extractors/csharp.ts +448 -0
- package/src/extractors/cuda.ts +433 -0
- package/src/extractors/dart.ts +304 -0
- package/src/extractors/elixir.ts +350 -0
- package/src/extractors/erlang.ts +295 -0
- package/src/extractors/fsharp.ts +357 -0
- package/src/extractors/gleam.ts +247 -0
- package/src/extractors/go.ts +512 -0
- package/src/extractors/groovy.ts +436 -0
- package/src/extractors/haskell.ts +276 -0
- package/src/extractors/hcl.ts +136 -0
- package/src/extractors/helpers.ts +449 -0
- package/src/extractors/index.ts +32 -0
- package/src/extractors/java.ts +439 -0
- package/src/extractors/javascript.ts +3844 -0
- package/src/extractors/julia.ts +432 -0
- package/src/extractors/kotlin.ts +358 -0
- package/src/extractors/lua.ts +169 -0
- package/src/extractors/objc.ts +555 -0
- package/src/extractors/ocaml.ts +337 -0
- package/src/extractors/php.ts +399 -0
- package/src/extractors/python.ts +439 -0
- package/src/extractors/r.ts +275 -0
- package/src/extractors/ruby.ts +292 -0
- package/src/extractors/rust.ts +371 -0
- package/src/extractors/scala.ts +335 -0
- package/src/extractors/solidity.ts +375 -0
- package/src/extractors/swift.ts +337 -0
- package/src/extractors/verilog.ts +383 -0
- package/src/extractors/zig.ts +291 -0
- package/src/features/ast.ts +405 -0
- package/src/features/audit.ts +446 -0
- package/src/features/batch.ts +182 -0
- package/src/features/boundaries.ts +345 -0
- package/src/features/branch-compare.ts +618 -0
- package/src/features/cfg.ts +750 -0
- package/src/features/check.ts +454 -0
- package/src/features/cochange.ts +506 -0
- package/src/features/communities.ts +232 -0
- package/src/features/complexity-query.ts +388 -0
- package/src/features/complexity.ts +753 -0
- package/src/features/dataflow.ts +1654 -0
- package/src/features/export.ts +486 -0
- package/src/features/flow.ts +307 -0
- package/src/features/graph-enrichment.ts +493 -0
- package/src/features/manifesto.ts +508 -0
- package/src/features/owners.ts +360 -0
- package/src/features/sequence.ts +410 -0
- package/src/features/shared/find-nodes.ts +28 -0
- package/src/features/snapshot.ts +221 -0
- package/src/features/structure-query.ts +456 -0
- package/src/features/structure.ts +1100 -0
- package/src/features/triage.ts +199 -0
- package/src/graph/algorithms/bfs.ts +92 -0
- package/src/graph/algorithms/centrality.ts +50 -0
- package/src/graph/algorithms/index.ts +6 -0
- package/src/graph/algorithms/leiden/LICENSE +24 -0
- package/src/graph/algorithms/leiden/adapter.ts +252 -0
- package/src/graph/algorithms/leiden/cpm.ts +77 -0
- package/src/graph/algorithms/leiden/index.ts +224 -0
- package/src/graph/algorithms/leiden/modularity.ts +122 -0
- package/src/graph/algorithms/leiden/optimiser.ts +706 -0
- package/src/graph/algorithms/leiden/partition.ts +577 -0
- package/src/graph/algorithms/leiden/rng.ts +20 -0
- package/src/graph/algorithms/louvain.ts +75 -0
- package/src/graph/algorithms/shortest-path.ts +59 -0
- package/src/graph/algorithms/tarjan.ts +55 -0
- package/src/graph/builders/dependency.ts +148 -0
- package/src/graph/builders/index.ts +3 -0
- package/src/graph/builders/structure.ts +58 -0
- package/src/graph/builders/temporal.ts +51 -0
- package/src/graph/classifiers/index.ts +2 -0
- package/src/graph/classifiers/risk.ts +123 -0
- package/src/graph/classifiers/roles.ts +226 -0
- package/src/graph/index.ts +13 -0
- package/src/graph/model.ts +238 -0
- package/src/index.cjs +16 -0
- package/src/index.ts +72 -0
- package/src/infrastructure/config.ts +954 -0
- package/src/infrastructure/logger.ts +24 -0
- package/src/infrastructure/native.ts +199 -0
- package/src/infrastructure/registry.ts +313 -0
- package/src/infrastructure/result-formatter.ts +2 -0
- package/src/infrastructure/suppress.ts +47 -0
- package/src/infrastructure/test-filter.ts +7 -0
- package/src/infrastructure/update-check.ts +196 -0
- package/src/mcp/index.ts +2 -0
- package/src/mcp/middleware.ts +38 -0
- package/src/mcp/server.ts +259 -0
- package/src/mcp/tool-registry.ts +890 -0
- package/src/mcp/tools/ast-query.ts +25 -0
- package/src/mcp/tools/audit.ts +33 -0
- package/src/mcp/tools/batch-query.ts +22 -0
- package/src/mcp/tools/branch-compare.ts +22 -0
- package/src/mcp/tools/brief.ts +15 -0
- package/src/mcp/tools/cfg.ts +32 -0
- package/src/mcp/tools/check.ts +60 -0
- package/src/mcp/tools/co-changes.ts +29 -0
- package/src/mcp/tools/code-owners.ts +22 -0
- package/src/mcp/tools/communities.ts +25 -0
- package/src/mcp/tools/complexity.ts +30 -0
- package/src/mcp/tools/context.ts +30 -0
- package/src/mcp/tools/dataflow.ts +38 -0
- package/src/mcp/tools/diff-impact.ts +35 -0
- package/src/mcp/tools/execution-flow.ts +38 -0
- package/src/mcp/tools/export-graph.ts +67 -0
- package/src/mcp/tools/file-deps.ts +20 -0
- package/src/mcp/tools/file-exports.ts +22 -0
- package/src/mcp/tools/find-cycles.ts +19 -0
- package/src/mcp/tools/fn-impact.ts +26 -0
- package/src/mcp/tools/impact-analysis.ts +15 -0
- package/src/mcp/tools/implementations.ts +24 -0
- package/src/mcp/tools/index.ts +81 -0
- package/src/mcp/tools/interfaces.ts +24 -0
- package/src/mcp/tools/list-functions.ts +23 -0
- package/src/mcp/tools/list-repos.ts +17 -0
- package/src/mcp/tools/module-map.ts +13 -0
- package/src/mcp/tools/node-roles.ts +23 -0
- package/src/mcp/tools/path.ts +34 -0
- package/src/mcp/tools/query.ts +47 -0
- package/src/mcp/tools/semantic-search.ts +76 -0
- package/src/mcp/tools/sequence.ts +30 -0
- package/src/mcp/tools/structure.ts +25 -0
- package/src/mcp/tools/symbol-children.ts +24 -0
- package/src/mcp/tools/triage.ts +50 -0
- package/src/mcp/tools/where.ts +22 -0
- package/src/mcp/types.ts +21 -0
- package/src/presentation/audit.ts +109 -0
- package/src/presentation/batch.ts +50 -0
- package/src/presentation/branch-compare.ts +148 -0
- package/src/presentation/brief.ts +63 -0
- package/src/presentation/cfg.ts +103 -0
- package/src/presentation/check.ts +138 -0
- package/src/presentation/cochange.ts +57 -0
- package/src/presentation/colors.ts +51 -0
- package/src/presentation/communities.ts +114 -0
- package/src/presentation/complexity.ts +127 -0
- package/src/presentation/dataflow.ts +179 -0
- package/src/presentation/diff-impact-mermaid.ts +169 -0
- package/src/presentation/export.ts +509 -0
- package/src/presentation/flow.ts +147 -0
- package/src/presentation/manifesto.ts +92 -0
- package/src/presentation/owners.ts +89 -0
- package/src/presentation/queries-cli/exports.ts +152 -0
- package/src/presentation/queries-cli/impact.ts +340 -0
- package/src/presentation/queries-cli/index.ts +13 -0
- package/src/presentation/queries-cli/inspect.ts +594 -0
- package/src/presentation/queries-cli/overview.ts +375 -0
- package/src/presentation/queries-cli/path.ts +187 -0
- package/src/presentation/queries-cli.ts +30 -0
- package/src/presentation/query.ts +17 -0
- package/src/presentation/result-formatter.ts +161 -0
- package/src/presentation/sequence-renderer.ts +46 -0
- package/src/presentation/sequence.ts +43 -0
- package/src/presentation/structure.ts +109 -0
- package/src/presentation/table.ts +45 -0
- package/src/presentation/triage.ts +84 -0
- package/src/presentation/viewer.ts +715 -0
- package/src/shared/constants.ts +70 -0
- package/src/shared/errors.ts +85 -0
- package/src/shared/file-utils.ts +233 -0
- package/src/shared/generators.ts +176 -0
- package/src/shared/globs.ts +121 -0
- package/src/shared/hierarchy.ts +81 -0
- package/src/shared/kinds.ts +85 -0
- package/src/shared/normalize.ts +106 -0
- package/src/shared/paginate.ts +93 -0
- package/src/shared/version.ts +10 -0
- package/src/types.ts +2871 -0
|
@@ -0,0 +1,3844 @@
|
|
|
1
|
+
import { debug } from '../infrastructure/logger.js';
|
|
2
|
+
import type {
|
|
3
|
+
ArrayCallbackBinding,
|
|
4
|
+
ArrayElemBinding,
|
|
5
|
+
Call,
|
|
6
|
+
CallAssignment,
|
|
7
|
+
ClassRelation,
|
|
8
|
+
Definition,
|
|
9
|
+
DynamicKind,
|
|
10
|
+
Export,
|
|
11
|
+
ExtractorOutput,
|
|
12
|
+
FnRefBinding,
|
|
13
|
+
ForOfBinding,
|
|
14
|
+
Import,
|
|
15
|
+
ObjectPropBinding,
|
|
16
|
+
ObjectRestParamBinding,
|
|
17
|
+
ParamBinding,
|
|
18
|
+
SpreadArgBinding,
|
|
19
|
+
SubDeclaration,
|
|
20
|
+
ThisCallBinding,
|
|
21
|
+
TreeSitterNode,
|
|
22
|
+
TreeSitterQuery,
|
|
23
|
+
TreeSitterTree,
|
|
24
|
+
TypeMapEntry,
|
|
25
|
+
} from '../types.js';
|
|
26
|
+
import {
|
|
27
|
+
findChild,
|
|
28
|
+
findParentNode,
|
|
29
|
+
MAX_WALK_DEPTH,
|
|
30
|
+
nodeEndLine,
|
|
31
|
+
nodeStartLine,
|
|
32
|
+
setTypeMapEntry,
|
|
33
|
+
} from './helpers.js';
|
|
34
|
+
|
|
35
|
+
/** Built-in globals that start with uppercase but are not user-defined types. */
|
|
36
|
+
const BUILTIN_GLOBALS: Set<string> = new Set([
|
|
37
|
+
'Math',
|
|
38
|
+
'JSON',
|
|
39
|
+
'Promise',
|
|
40
|
+
'Array',
|
|
41
|
+
'Object',
|
|
42
|
+
'Date',
|
|
43
|
+
'Error',
|
|
44
|
+
'Symbol',
|
|
45
|
+
'Map',
|
|
46
|
+
'Set',
|
|
47
|
+
'RegExp',
|
|
48
|
+
'Number',
|
|
49
|
+
'String',
|
|
50
|
+
'Boolean',
|
|
51
|
+
'WeakMap',
|
|
52
|
+
'WeakSet',
|
|
53
|
+
'WeakRef',
|
|
54
|
+
'Proxy',
|
|
55
|
+
'Reflect',
|
|
56
|
+
'Intl',
|
|
57
|
+
'ArrayBuffer',
|
|
58
|
+
'SharedArrayBuffer',
|
|
59
|
+
'DataView',
|
|
60
|
+
'Atomics',
|
|
61
|
+
'BigInt',
|
|
62
|
+
'Float32Array',
|
|
63
|
+
'Float64Array',
|
|
64
|
+
'Int8Array',
|
|
65
|
+
'Int16Array',
|
|
66
|
+
'Int32Array',
|
|
67
|
+
'Uint8Array',
|
|
68
|
+
'Uint16Array',
|
|
69
|
+
'Uint32Array',
|
|
70
|
+
'Uint8ClampedArray',
|
|
71
|
+
'URL',
|
|
72
|
+
'URLSearchParams',
|
|
73
|
+
'TextEncoder',
|
|
74
|
+
'TextDecoder',
|
|
75
|
+
'AbortController',
|
|
76
|
+
'AbortSignal',
|
|
77
|
+
'Headers',
|
|
78
|
+
'Request',
|
|
79
|
+
'Response',
|
|
80
|
+
'FormData',
|
|
81
|
+
'Blob',
|
|
82
|
+
'File',
|
|
83
|
+
'ReadableStream',
|
|
84
|
+
'WritableStream',
|
|
85
|
+
'TransformStream',
|
|
86
|
+
'console',
|
|
87
|
+
'Buffer',
|
|
88
|
+
'EventEmitter',
|
|
89
|
+
'Stream',
|
|
90
|
+
'process',
|
|
91
|
+
'window',
|
|
92
|
+
'document',
|
|
93
|
+
'globalThis',
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
/** Maximum chain depth for inter-procedural return-type propagation (Phase 8.2). */
|
|
97
|
+
const MAX_PROPAGATION_DEPTH = 3;
|
|
98
|
+
/** Confidence penalty applied per propagation hop (1.0 → 0.9 → 0.8 → 0.7). */
|
|
99
|
+
export const PROPAGATION_HOP_PENALTY = 0.1;
|
|
100
|
+
/**
|
|
101
|
+
* Confidence score for a return type inferred from `return new Constructor()` with no
|
|
102
|
+
* explicit TypeScript annotation. Registered as `analysis.typeInferenceConfidence` in
|
|
103
|
+
* `src/infrastructure/config.ts` DEFAULTS — kept in sync manually until config is
|
|
104
|
+
* threaded through to `extractSymbols`.
|
|
105
|
+
*/
|
|
106
|
+
const INFERRED_RETURN_TYPE_CONFIDENCE = 0.85;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extract symbols from a JS/TS parsed AST.
|
|
110
|
+
* When a compiled tree-sitter Query is provided (from parser.js),
|
|
111
|
+
* uses the fast query-based path. Falls back to manual tree walk otherwise.
|
|
112
|
+
*/
|
|
113
|
+
export function extractSymbols(
|
|
114
|
+
tree: TreeSitterTree,
|
|
115
|
+
_filePath: string,
|
|
116
|
+
query?: TreeSitterQuery,
|
|
117
|
+
): ExtractorOutput {
|
|
118
|
+
if (query) return extractSymbolsQuery(tree, query);
|
|
119
|
+
return extractSymbolsWalk(tree);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Query-based extraction (fast path) ──────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
/** Handle function_declaration capture. */
|
|
125
|
+
function handleFnCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
126
|
+
const fnChildren = extractParameters(c.fn_node!);
|
|
127
|
+
definitions.push({
|
|
128
|
+
name: c.fn_name!.text,
|
|
129
|
+
kind: 'function',
|
|
130
|
+
line: nodeStartLine(c.fn_node!),
|
|
131
|
+
endLine: nodeEndLine(c.fn_node!),
|
|
132
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Handle variable_declarator with arrow_function / function_expression capture. */
|
|
137
|
+
function handleVarFnCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
138
|
+
const declNode = c.varfn_name!.parent?.parent;
|
|
139
|
+
const line = declNode ? nodeStartLine(declNode) : nodeStartLine(c.varfn_name!);
|
|
140
|
+
const varFnChildren = extractParameters(c.varfn_value!);
|
|
141
|
+
definitions.push({
|
|
142
|
+
name: c.varfn_name!.text,
|
|
143
|
+
kind: 'function',
|
|
144
|
+
line,
|
|
145
|
+
endLine: nodeEndLine(c.varfn_value!),
|
|
146
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Handle class_declaration capture. */
|
|
151
|
+
function handleClassCapture(
|
|
152
|
+
c: Record<string, TreeSitterNode>,
|
|
153
|
+
definitions: Definition[],
|
|
154
|
+
classes: ClassRelation[],
|
|
155
|
+
): void {
|
|
156
|
+
const className = c.cls_name!.text;
|
|
157
|
+
const startLine = nodeStartLine(c.cls_node!);
|
|
158
|
+
const clsChildren = extractClassProperties(c.cls_node!);
|
|
159
|
+
definitions.push({
|
|
160
|
+
name: className,
|
|
161
|
+
kind: 'class',
|
|
162
|
+
line: startLine,
|
|
163
|
+
endLine: nodeEndLine(c.cls_node!),
|
|
164
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
165
|
+
});
|
|
166
|
+
const heritage =
|
|
167
|
+
c.cls_node!.childForFieldName('heritage') || findChild(c.cls_node!, 'class_heritage');
|
|
168
|
+
if (heritage) {
|
|
169
|
+
const superName = extractSuperclass(heritage);
|
|
170
|
+
if (superName) classes.push({ name: className, extends: superName, line: startLine });
|
|
171
|
+
const implementsList = extractImplements(heritage);
|
|
172
|
+
for (const iface of implementsList) {
|
|
173
|
+
classes.push({ name: className, implements: iface, line: startLine });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Handle method_definition capture. */
|
|
179
|
+
function handleMethodCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
180
|
+
const methNameNode = c.meth_name!;
|
|
181
|
+
let methName: string;
|
|
182
|
+
if (methNameNode.type === 'computed_property_name') {
|
|
183
|
+
// Extract the inner string literal from `['methodName']` or `["methodName"]`.
|
|
184
|
+
// Non-string computed keys (e.g. `[Symbol.iterator]`) cannot be resolved at
|
|
185
|
+
// dot-notation call sites, so skip them entirely.
|
|
186
|
+
const inner = methNameNode.child(1); // child(0)='[', child(1)=string, child(2)=']'
|
|
187
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) return;
|
|
188
|
+
methName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
189
|
+
if (!methName) return;
|
|
190
|
+
} else {
|
|
191
|
+
methName = methNameNode.text;
|
|
192
|
+
}
|
|
193
|
+
const parentClass = findParentClass(c.meth_node!);
|
|
194
|
+
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
195
|
+
const methChildren = extractParameters(c.meth_node!);
|
|
196
|
+
const methVis = extractVisibility(c.meth_node!);
|
|
197
|
+
definitions.push({
|
|
198
|
+
name: fullName,
|
|
199
|
+
kind: 'method',
|
|
200
|
+
line: nodeStartLine(c.meth_node!),
|
|
201
|
+
endLine: nodeEndLine(c.meth_node!),
|
|
202
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
203
|
+
visibility: methVis,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Handle export_statement capture. */
|
|
208
|
+
function handleExportCapture(
|
|
209
|
+
c: Record<string, TreeSitterNode>,
|
|
210
|
+
exps: Export[],
|
|
211
|
+
imports: Import[],
|
|
212
|
+
): void {
|
|
213
|
+
const exportLine = nodeStartLine(c.exp_node!);
|
|
214
|
+
const decl = c.exp_node!.childForFieldName('declaration');
|
|
215
|
+
if (decl) {
|
|
216
|
+
const declType = decl.type;
|
|
217
|
+
const kindMap: Record<string, string> = {
|
|
218
|
+
function_declaration: 'function',
|
|
219
|
+
generator_function_declaration: 'function',
|
|
220
|
+
class_declaration: 'class',
|
|
221
|
+
abstract_class_declaration: 'class',
|
|
222
|
+
interface_declaration: 'interface',
|
|
223
|
+
type_alias_declaration: 'type',
|
|
224
|
+
};
|
|
225
|
+
const kind = kindMap[declType];
|
|
226
|
+
if (kind) {
|
|
227
|
+
const n = decl.childForFieldName('name');
|
|
228
|
+
if (n) exps.push({ name: n.text, kind: kind as Export['kind'], line: exportLine });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const source = c.exp_node!.childForFieldName('source') || findChild(c.exp_node!, 'string');
|
|
232
|
+
if (source && !decl) {
|
|
233
|
+
const modPath = source.text.replace(/['"]/g, '');
|
|
234
|
+
const reexportNames = extractImportNames(c.exp_node!);
|
|
235
|
+
const nodeText = c.exp_node!.text;
|
|
236
|
+
const isWildcard = nodeText.includes('export *') || nodeText.includes('export*');
|
|
237
|
+
imports.push({
|
|
238
|
+
source: modPath,
|
|
239
|
+
names: reexportNames,
|
|
240
|
+
line: exportLine,
|
|
241
|
+
reexport: true,
|
|
242
|
+
wildcardReexport: isWildcard && reexportNames.length === 0,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function handleInterfaceCapture(
|
|
248
|
+
c: Record<string, TreeSitterNode>,
|
|
249
|
+
definitions: Definition[],
|
|
250
|
+
): void {
|
|
251
|
+
const ifaceNode = c.iface_node!;
|
|
252
|
+
const ifaceName = c.iface_name!.text;
|
|
253
|
+
definitions.push({
|
|
254
|
+
name: ifaceName,
|
|
255
|
+
kind: 'interface',
|
|
256
|
+
line: nodeStartLine(ifaceNode),
|
|
257
|
+
endLine: nodeEndLine(ifaceNode),
|
|
258
|
+
});
|
|
259
|
+
const body =
|
|
260
|
+
ifaceNode.childForFieldName('body') ||
|
|
261
|
+
findChild(ifaceNode, 'interface_body') ||
|
|
262
|
+
findChild(ifaceNode, 'object_type');
|
|
263
|
+
if (body) extractInterfaceMethods(body, ifaceName, definitions);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function handleTypeCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
267
|
+
const typeNode = c.type_node!;
|
|
268
|
+
definitions.push({
|
|
269
|
+
name: c.type_name!.text,
|
|
270
|
+
kind: 'type',
|
|
271
|
+
line: nodeStartLine(typeNode),
|
|
272
|
+
endLine: nodeEndLine(typeNode),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function handleImportCapture(c: Record<string, TreeSitterNode>, imports: Import[]): void {
|
|
277
|
+
const impNode = c.imp_node!;
|
|
278
|
+
const isTypeOnly = impNode.text.startsWith('import type');
|
|
279
|
+
const modPath = c.imp_source!.text.replace(/['"]/g, '');
|
|
280
|
+
const names = extractImportNames(impNode);
|
|
281
|
+
imports.push({
|
|
282
|
+
source: modPath,
|
|
283
|
+
names,
|
|
284
|
+
line: nodeStartLine(impNode),
|
|
285
|
+
typeOnly: isTypeOnly,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Dispatch a single query match to the appropriate handler. */
|
|
290
|
+
function dispatchQueryMatch(
|
|
291
|
+
c: Record<string, TreeSitterNode>,
|
|
292
|
+
definitions: Definition[],
|
|
293
|
+
calls: Call[],
|
|
294
|
+
imports: Import[],
|
|
295
|
+
classes: ClassRelation[],
|
|
296
|
+
exps: Export[],
|
|
297
|
+
): void {
|
|
298
|
+
if (c.fn_node) {
|
|
299
|
+
handleFnCapture(c, definitions);
|
|
300
|
+
} else if (c.varfn_name) {
|
|
301
|
+
handleVarFnCapture(c, definitions);
|
|
302
|
+
} else if (c.cls_node) {
|
|
303
|
+
handleClassCapture(c, definitions, classes);
|
|
304
|
+
} else if (c.meth_node) {
|
|
305
|
+
handleMethodCapture(c, definitions);
|
|
306
|
+
} else if (c.iface_node) {
|
|
307
|
+
handleInterfaceCapture(c, definitions);
|
|
308
|
+
} else if (c.type_node) {
|
|
309
|
+
handleTypeCapture(c, definitions);
|
|
310
|
+
} else if (c.imp_node) {
|
|
311
|
+
handleImportCapture(c, imports);
|
|
312
|
+
} else if (c.exp_node) {
|
|
313
|
+
handleExportCapture(c, exps, imports);
|
|
314
|
+
} else if (c.callfn_node) {
|
|
315
|
+
// Route through extractCallInfo so special identifier calls (eval) get classified.
|
|
316
|
+
const callfnInfo = extractCallInfo(c.callfn_name!, c.callfn_node);
|
|
317
|
+
if (callfnInfo) calls.push(callfnInfo);
|
|
318
|
+
calls.push(...extractCallbackReferenceCalls(c.callfn_node));
|
|
319
|
+
} else if (c.callmem_node) {
|
|
320
|
+
const callInfo = extractCallInfo(c.callmem_fn!, c.callmem_node);
|
|
321
|
+
if (callInfo) calls.push(callInfo);
|
|
322
|
+
const cbDef = extractCallbackDefinition(c.callmem_node, c.callmem_fn);
|
|
323
|
+
if (cbDef) definitions.push(cbDef);
|
|
324
|
+
calls.push(...extractCallbackReferenceCalls(c.callmem_node));
|
|
325
|
+
} else if (c.callsub_node) {
|
|
326
|
+
const callInfo = extractCallInfo(c.callsub_fn!, c.callsub_node);
|
|
327
|
+
if (callInfo) calls.push(callInfo);
|
|
328
|
+
calls.push(...extractCallbackReferenceCalls(c.callsub_node));
|
|
329
|
+
} else if (c.newfn_node) {
|
|
330
|
+
if (c.newfn_name!.text === 'Function') {
|
|
331
|
+
// new Function(body) — dynamic code execution; classify as eval kind
|
|
332
|
+
calls.push({
|
|
333
|
+
name: '<dynamic:eval>',
|
|
334
|
+
line: nodeStartLine(c.newfn_node),
|
|
335
|
+
dynamic: true,
|
|
336
|
+
dynamicKind: 'eval',
|
|
337
|
+
});
|
|
338
|
+
} else {
|
|
339
|
+
calls.push({
|
|
340
|
+
name: c.newfn_name!.text,
|
|
341
|
+
line: nodeStartLine(c.newfn_node),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
} else if (c.newmem_node) {
|
|
345
|
+
const callInfo = extractCallInfo(c.newmem_fn!, c.newmem_node);
|
|
346
|
+
if (callInfo) calls.push(callInfo);
|
|
347
|
+
} else if (c.assign_node) {
|
|
348
|
+
handleCommonJSAssignment(c.assign_left!, c.assign_right!, c.assign_node, imports);
|
|
349
|
+
handleFuncPropAssignment(c.assign_left!, c.assign_right!, definitions);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): ExtractorOutput {
|
|
354
|
+
const definitions: Definition[] = [];
|
|
355
|
+
const calls: Call[] = [];
|
|
356
|
+
const imports: Import[] = [];
|
|
357
|
+
const classes: ClassRelation[] = [];
|
|
358
|
+
const exps: Export[] = [];
|
|
359
|
+
const typeMap: Map<string, TypeMapEntry> = new Map();
|
|
360
|
+
const returnTypeMap: Map<string, TypeMapEntry> = new Map();
|
|
361
|
+
const callAssignments: CallAssignment[] = [];
|
|
362
|
+
const fnRefBindings: FnRefBinding[] = [];
|
|
363
|
+
const paramBindings: ParamBinding[] = [];
|
|
364
|
+
const arrayElemBindings: ArrayElemBinding[] = [];
|
|
365
|
+
const spreadArgBindings: SpreadArgBinding[] = [];
|
|
366
|
+
const forOfBindings: ForOfBinding[] = [];
|
|
367
|
+
const arrayCallbackBindings: ArrayCallbackBinding[] = [];
|
|
368
|
+
const objectRestParamBindings: ObjectRestParamBinding[] = [];
|
|
369
|
+
const objectPropBindings: ObjectPropBinding[] = [];
|
|
370
|
+
const thisCallBindings: ThisCallBinding[] = [];
|
|
371
|
+
|
|
372
|
+
const matches = query.matches(tree.rootNode);
|
|
373
|
+
|
|
374
|
+
for (const match of matches) {
|
|
375
|
+
// Build capture lookup for this match (1-3 captures each, very fast)
|
|
376
|
+
const c: Record<string, TreeSitterNode> = Object.create(null);
|
|
377
|
+
for (const cap of match.captures) c[cap.name] = cap.node;
|
|
378
|
+
dispatchQueryMatch(c, definitions, calls, imports, classes, exps);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Extract top-level constants via targeted walk (query patterns don't cover these)
|
|
382
|
+
extractConstantsWalk(tree.rootNode, definitions);
|
|
383
|
+
|
|
384
|
+
// Phase 8.2: Extract function return types first — runContextCollectorWalk's
|
|
385
|
+
// declarator handler reads the *complete* per-file map for inter-procedural
|
|
386
|
+
// propagation, so this cannot be folded into that pass.
|
|
387
|
+
extractReturnTypeMapWalk(tree.rootNode, returnTypeMap);
|
|
388
|
+
|
|
389
|
+
// Context-tracking collector pass: typeMap (with return-type propagation),
|
|
390
|
+
// object-rest param bindings, and spread/for-of/Array.from bindings.
|
|
391
|
+
runContextCollectorWalk(tree.rootNode, {
|
|
392
|
+
typeMap,
|
|
393
|
+
returnTypeMap,
|
|
394
|
+
callAssignments,
|
|
395
|
+
fnRefBindings,
|
|
396
|
+
objectRestParamBindings,
|
|
397
|
+
spreadArgBindings,
|
|
398
|
+
forOfBindings,
|
|
399
|
+
arrayCallbackBindings,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Extract definitions from destructured bindings (query patterns don't match object_pattern)
|
|
403
|
+
extractDestructuredBindingsWalk(tree.rootNode, definitions);
|
|
404
|
+
|
|
405
|
+
// Everything without bespoke traversal semantics is collected in ONE pass:
|
|
406
|
+
// dynamic import() calls, prototype-method definitions, param bindings,
|
|
407
|
+
// array-element bindings, object-prop bindings, `new X()` names,
|
|
408
|
+
// Object.defineProperty receivers, class members (fields/static blocks,
|
|
409
|
+
// which query patterns don't capture), and this()/call/apply bindings.
|
|
410
|
+
const newExpressions: string[] = [];
|
|
411
|
+
const definePropertyReceivers: Map<string, string> = new Map();
|
|
412
|
+
runCollectorWalk(tree.rootNode, {
|
|
413
|
+
definitions,
|
|
414
|
+
typeMap,
|
|
415
|
+
paramBindings,
|
|
416
|
+
arrayElemBindings,
|
|
417
|
+
objectPropBindings,
|
|
418
|
+
newExpressions,
|
|
419
|
+
definePropertyReceivers,
|
|
420
|
+
imports,
|
|
421
|
+
calls,
|
|
422
|
+
thisCallBindings,
|
|
423
|
+
classMemberDefs: definitions,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
definitions,
|
|
428
|
+
calls,
|
|
429
|
+
imports,
|
|
430
|
+
classes,
|
|
431
|
+
exports: exps,
|
|
432
|
+
typeMap,
|
|
433
|
+
returnTypeMap,
|
|
434
|
+
callAssignments,
|
|
435
|
+
fnRefBindings,
|
|
436
|
+
paramBindings,
|
|
437
|
+
arrayElemBindings,
|
|
438
|
+
spreadArgBindings,
|
|
439
|
+
forOfBindings,
|
|
440
|
+
arrayCallbackBindings,
|
|
441
|
+
objectRestParamBindings,
|
|
442
|
+
objectPropBindings,
|
|
443
|
+
thisCallBindings,
|
|
444
|
+
newExpressions,
|
|
445
|
+
...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** Node types that define a function scope — constants inside these are skipped. */
|
|
450
|
+
const FUNCTION_SCOPE_TYPES = new Set([
|
|
451
|
+
'function_declaration',
|
|
452
|
+
'arrow_function',
|
|
453
|
+
'function_expression',
|
|
454
|
+
'method_definition',
|
|
455
|
+
'generator_function_declaration',
|
|
456
|
+
'generator_function',
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Return true when `node` has an ancestor whose type is in FUNCTION_SCOPE_TYPES.
|
|
461
|
+
* Used by the walk path to skip declarations inside function bodies, matching
|
|
462
|
+
* the query path's top-down FUNCTION_SCOPE_TYPES filter.
|
|
463
|
+
*/
|
|
464
|
+
function hasFunctionScopeAncestor(node: TreeSitterNode): boolean {
|
|
465
|
+
let p: TreeSitterNode | null = node.parent ?? null;
|
|
466
|
+
while (p) {
|
|
467
|
+
if (FUNCTION_SCOPE_TYPES.has(p.type)) return true;
|
|
468
|
+
p = p.parent ?? null;
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Recursively walk the AST to extract `const x = <literal>` as constants.
|
|
475
|
+
* Skips nodes inside function scopes so only file-level / block-level constants
|
|
476
|
+
* are captured — matching the native engine's behaviour.
|
|
477
|
+
*/
|
|
478
|
+
function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]): void {
|
|
479
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
480
|
+
const child = node.child(i);
|
|
481
|
+
if (!child) continue;
|
|
482
|
+
|
|
483
|
+
// Don't descend into function scopes
|
|
484
|
+
if (FUNCTION_SCOPE_TYPES.has(child.type)) continue;
|
|
485
|
+
|
|
486
|
+
let declNode = child;
|
|
487
|
+
// Handle `export const …` — unwrap the export_statement to its declaration child
|
|
488
|
+
if (child.type === 'export_statement') {
|
|
489
|
+
const inner = child.childForFieldName('declaration');
|
|
490
|
+
if (inner) declNode = inner;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
extractConstDeclarators(declNode, definitions);
|
|
494
|
+
extractLetVarObjLiteralDeclarators(declNode, definitions);
|
|
495
|
+
|
|
496
|
+
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
497
|
+
if (child.type !== 'export_statement') {
|
|
498
|
+
extractConstantsWalk(child, definitions);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Class field definitions and static initializer blocks (which query patterns
|
|
504
|
+
// don't capture) are collected inline in runCollectorWalk's field_definition /
|
|
505
|
+
// class_static_block cases when `classMemberDefs` is set. The walk-based path
|
|
506
|
+
// (extractSymbolsWalk) handles these node types via walkJavaScriptNode instead.
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
|
|
510
|
+
* e.g. `const { handleToken, checkPermissions } = initAuth(config)`
|
|
511
|
+
*/
|
|
512
|
+
function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Definition[]): void {
|
|
513
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
514
|
+
const child = node.child(i);
|
|
515
|
+
if (!child) continue;
|
|
516
|
+
if (FUNCTION_SCOPE_TYPES.has(child.type)) continue;
|
|
517
|
+
|
|
518
|
+
let declNode = child;
|
|
519
|
+
if (child.type === 'export_statement') {
|
|
520
|
+
const inner = child.childForFieldName('declaration');
|
|
521
|
+
if (inner) declNode = inner;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const t = declNode.type;
|
|
525
|
+
if (
|
|
526
|
+
(t === 'lexical_declaration' || t === 'variable_declaration') &&
|
|
527
|
+
declNode.text.startsWith('const ')
|
|
528
|
+
) {
|
|
529
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
530
|
+
const declarator = declNode.child(j);
|
|
531
|
+
if (declarator?.type !== 'variable_declarator') continue;
|
|
532
|
+
const nameN = declarator.childForFieldName('name');
|
|
533
|
+
if (nameN && nameN.type === 'object_pattern') {
|
|
534
|
+
extractDestructuredBindings(
|
|
535
|
+
nameN,
|
|
536
|
+
nodeStartLine(declNode),
|
|
537
|
+
nodeEndLine(declNode),
|
|
538
|
+
definitions,
|
|
539
|
+
);
|
|
540
|
+
} else if (nameN && nameN.type === 'array_pattern') {
|
|
541
|
+
// `const [x, y] = ...` — emit a single constant node whose name is the
|
|
542
|
+
// full array pattern text (e.g. `[x, y]`), matching native engine behaviour.
|
|
543
|
+
definitions.push({
|
|
544
|
+
name: nameN.text,
|
|
545
|
+
kind: 'constant',
|
|
546
|
+
line: nodeStartLine(declNode),
|
|
547
|
+
endLine: nodeEndLine(declNode),
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (child.type !== 'export_statement') {
|
|
554
|
+
extractDestructuredBindingsWalk(child, definitions);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** Extract constant definitions from a `const` declaration node. */
|
|
560
|
+
function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definition[]): void {
|
|
561
|
+
const t = declNode.type;
|
|
562
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
|
|
563
|
+
if (!declNode.text.startsWith('const ')) return;
|
|
564
|
+
|
|
565
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
566
|
+
const declarator = declNode.child(j);
|
|
567
|
+
if (declarator?.type !== 'variable_declarator') continue;
|
|
568
|
+
const nameN = declarator.childForFieldName('name');
|
|
569
|
+
const valueN = declarator.childForFieldName('value');
|
|
570
|
+
if (nameN?.type !== 'identifier' || !valueN) continue;
|
|
571
|
+
// Skip functions — already captured by query patterns
|
|
572
|
+
const valType = valueN.type;
|
|
573
|
+
if (
|
|
574
|
+
valType === 'arrow_function' ||
|
|
575
|
+
valType === 'function_expression' ||
|
|
576
|
+
valType === 'function' ||
|
|
577
|
+
valType === 'generator_function'
|
|
578
|
+
)
|
|
579
|
+
continue;
|
|
580
|
+
if (isConstantValue(valueN)) {
|
|
581
|
+
definitions.push({
|
|
582
|
+
name: nameN.text,
|
|
583
|
+
kind: 'constant',
|
|
584
|
+
line: nodeStartLine(declNode),
|
|
585
|
+
endLine: nodeEndLine(declNode),
|
|
586
|
+
});
|
|
587
|
+
// Phase 8.3f: extract function/arrow properties from object literals.
|
|
588
|
+
// Scope guard: extractConstDeclarators is only called from extractConstantsWalk, which
|
|
589
|
+
// already skips const declarations inside function scopes (line ~412). So these definitions
|
|
590
|
+
// are always top-level. Any new call site must add a hasFunctionScopeAncestor guard
|
|
591
|
+
// (the walk path at handleVariableDecl does this).
|
|
592
|
+
if (valueN.type === 'object') {
|
|
593
|
+
extractObjectLiteralFunctions(valueN, nameN.text, definitions);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Extract qualified method definitions from `let`/`var` object-literal declarations.
|
|
601
|
+
* Mirrors `match_js_objlit_qualified_method_defs` in `javascript.rs`, which emits
|
|
602
|
+
* qualified definitions for `method_definition` (all declaration kinds) and
|
|
603
|
+
* `pair+arrow/function` (`let`/`var` only, since `const` is already handled by
|
|
604
|
+
* `extractConstDeclarators` → `extractObjectLiteralFunctions`).
|
|
605
|
+
*
|
|
606
|
+
* Called from extractConstantsWalk which already provides the function-scope guard.
|
|
607
|
+
* `var q1 = { m1() {} }` → emits Definition { name: 'q1.m1', kind: 'function' }
|
|
608
|
+
*/
|
|
609
|
+
function extractLetVarObjLiteralDeclarators(
|
|
610
|
+
declNode: TreeSitterNode,
|
|
611
|
+
definitions: Definition[],
|
|
612
|
+
): void {
|
|
613
|
+
const t = declNode.type;
|
|
614
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
|
|
615
|
+
if (declNode.text.startsWith('const ')) return; // handled by extractConstDeclarators
|
|
616
|
+
|
|
617
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
618
|
+
const declarator = declNode.child(j);
|
|
619
|
+
if (declarator?.type !== 'variable_declarator') continue;
|
|
620
|
+
const nameN = declarator.childForFieldName('name');
|
|
621
|
+
const valueN = declarator.childForFieldName('value');
|
|
622
|
+
if (nameN?.type !== 'identifier' || !valueN || valueN.type !== 'object') continue;
|
|
623
|
+
extractObjectLiteralFunctions(valueN, nameN.text, definitions);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Recursive walk to find dynamic import() calls.
|
|
629
|
+
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
630
|
+
* functions, but import() has function type `import` which none of those patterns cover.
|
|
631
|
+
*/
|
|
632
|
+
/**
|
|
633
|
+
* Collect a dynamic `import()` call at `node` (a call_expression).
|
|
634
|
+
* Returns true when the node *is* an import() call — the collector walk uses
|
|
635
|
+
* this to suppress dynamic-import collection inside the import's own argument
|
|
636
|
+
* subtree, preserving the former standalone walk's "don't recurse into
|
|
637
|
+
* import() children" behaviour without hiding those children from the other
|
|
638
|
+
* collectors.
|
|
639
|
+
*/
|
|
640
|
+
function collectDynamicImport(node: TreeSitterNode, imports: Import[]): boolean {
|
|
641
|
+
const fn = node.childForFieldName('function');
|
|
642
|
+
if (fn?.type !== 'import') return false;
|
|
643
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
644
|
+
if (args) {
|
|
645
|
+
const strArg = findChild(args, 'string');
|
|
646
|
+
if (strArg) {
|
|
647
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
648
|
+
const names = extractDynamicImportNames(node);
|
|
649
|
+
imports.push({
|
|
650
|
+
source: modPath,
|
|
651
|
+
names,
|
|
652
|
+
line: nodeStartLine(node),
|
|
653
|
+
dynamicImport: true,
|
|
654
|
+
});
|
|
655
|
+
} else {
|
|
656
|
+
debug(
|
|
657
|
+
`Skipping non-static dynamic import() at line ${nodeStartLine(node)} (template literal or variable)`,
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function handleCommonJSAssignment(
|
|
665
|
+
left: TreeSitterNode,
|
|
666
|
+
right: TreeSitterNode,
|
|
667
|
+
node: TreeSitterNode,
|
|
668
|
+
imports: Import[],
|
|
669
|
+
): void {
|
|
670
|
+
if (!left || !right) return;
|
|
671
|
+
const leftText = left.text;
|
|
672
|
+
if (!leftText.startsWith('module.exports') && leftText !== 'exports') return;
|
|
673
|
+
|
|
674
|
+
const assignLine = nodeStartLine(node);
|
|
675
|
+
|
|
676
|
+
// module.exports = require("…") — direct re-export
|
|
677
|
+
if (right.type === 'call_expression') {
|
|
678
|
+
extractRequireReexport(right, assignLine, imports);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// module.exports = { ...require("…") } — spread re-export
|
|
682
|
+
if (right.type === 'object') {
|
|
683
|
+
extractSpreadRequireReexports(right, assignLine, imports);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/** Extract a direct `require()` re-export from a call_expression. */
|
|
688
|
+
function extractRequireReexport(callExpr: TreeSitterNode, line: number, imports: Import[]): void {
|
|
689
|
+
const fn = callExpr.childForFieldName('function');
|
|
690
|
+
const args = callExpr.childForFieldName('arguments') || findChild(callExpr, 'arguments');
|
|
691
|
+
if (fn && fn.text === 'require' && args) {
|
|
692
|
+
const strArg = findChild(args, 'string');
|
|
693
|
+
if (strArg) {
|
|
694
|
+
imports.push({
|
|
695
|
+
source: strArg.text.replace(/['"]/g, ''),
|
|
696
|
+
names: [],
|
|
697
|
+
line,
|
|
698
|
+
reexport: true,
|
|
699
|
+
wildcardReexport: true,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/** Extract `...require()` re-exports from spread elements inside an object literal. */
|
|
706
|
+
function extractSpreadRequireReexports(
|
|
707
|
+
objectNode: TreeSitterNode,
|
|
708
|
+
line: number,
|
|
709
|
+
imports: Import[],
|
|
710
|
+
): void {
|
|
711
|
+
for (let ci = 0; ci < objectNode.childCount; ci++) {
|
|
712
|
+
const child = objectNode.child(ci);
|
|
713
|
+
if (child && child.type === 'spread_element') {
|
|
714
|
+
const spreadExpr = child.child(1) || child.childForFieldName('value');
|
|
715
|
+
if (spreadExpr && spreadExpr.type === 'call_expression') {
|
|
716
|
+
extractRequireReexport(spreadExpr, line, imports);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ── Manual tree walk (fallback when Query not available) ────────────────────
|
|
723
|
+
|
|
724
|
+
function extractSymbolsWalk(tree: TreeSitterTree): ExtractorOutput {
|
|
725
|
+
const ctx: ExtractorOutput = {
|
|
726
|
+
definitions: [],
|
|
727
|
+
calls: [],
|
|
728
|
+
imports: [],
|
|
729
|
+
classes: [],
|
|
730
|
+
exports: [],
|
|
731
|
+
typeMap: new Map(),
|
|
732
|
+
returnTypeMap: new Map(),
|
|
733
|
+
callAssignments: [],
|
|
734
|
+
fnRefBindings: [],
|
|
735
|
+
paramBindings: [],
|
|
736
|
+
arrayElemBindings: [],
|
|
737
|
+
spreadArgBindings: [],
|
|
738
|
+
forOfBindings: [],
|
|
739
|
+
arrayCallbackBindings: [],
|
|
740
|
+
objectRestParamBindings: [],
|
|
741
|
+
objectPropBindings: [],
|
|
742
|
+
thisCallBindings: [],
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
walkJavaScriptNode(tree.rootNode, ctx);
|
|
746
|
+
// Phase 8.2: Extract function return types first — runContextCollectorWalk's
|
|
747
|
+
// declarator handler reads the *complete* per-file map for inter-procedural
|
|
748
|
+
// propagation, so this cannot be folded into that pass.
|
|
749
|
+
extractReturnTypeMapWalk(tree.rootNode, ctx.returnTypeMap!);
|
|
750
|
+
// Context-tracking collector pass: typeMap (with return-type propagation),
|
|
751
|
+
// object-rest param bindings, and spread/for-of/Array.from bindings.
|
|
752
|
+
runContextCollectorWalk(tree.rootNode, {
|
|
753
|
+
typeMap: ctx.typeMap!,
|
|
754
|
+
returnTypeMap: ctx.returnTypeMap,
|
|
755
|
+
callAssignments: ctx.callAssignments,
|
|
756
|
+
fnRefBindings: ctx.fnRefBindings!,
|
|
757
|
+
objectRestParamBindings: ctx.objectRestParamBindings!,
|
|
758
|
+
spreadArgBindings: ctx.spreadArgBindings!,
|
|
759
|
+
forOfBindings: ctx.forOfBindings!,
|
|
760
|
+
arrayCallbackBindings: ctx.arrayCallbackBindings!,
|
|
761
|
+
});
|
|
762
|
+
// Single collector pass for everything else: prototype-method and func-prop
|
|
763
|
+
// definitions, param bindings, array-element bindings, object-prop bindings,
|
|
764
|
+
// `new X()` names, and Object.defineProperty receivers. Dynamic imports,
|
|
765
|
+
// this()/call/apply bindings, and class members are omitted here —
|
|
766
|
+
// walkJavaScriptNode already covers those node types on this path.
|
|
767
|
+
const newExpressions: string[] = [];
|
|
768
|
+
const definePropertyReceivers: Map<string, string> = new Map();
|
|
769
|
+
runCollectorWalk(tree.rootNode, {
|
|
770
|
+
definitions: ctx.definitions,
|
|
771
|
+
typeMap: ctx.typeMap!,
|
|
772
|
+
paramBindings: ctx.paramBindings!,
|
|
773
|
+
arrayElemBindings: ctx.arrayElemBindings!,
|
|
774
|
+
objectPropBindings: ctx.objectPropBindings!,
|
|
775
|
+
newExpressions,
|
|
776
|
+
definePropertyReceivers,
|
|
777
|
+
funcPropDefs: ctx.definitions,
|
|
778
|
+
});
|
|
779
|
+
ctx.newExpressions = newExpressions;
|
|
780
|
+
if (definePropertyReceivers.size > 0) ctx.definePropertyReceivers = definePropertyReceivers;
|
|
781
|
+
return ctx;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function walkJavaScriptNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
785
|
+
switch (node.type) {
|
|
786
|
+
case 'function_declaration':
|
|
787
|
+
case 'generator_function_declaration':
|
|
788
|
+
handleFunctionDecl(node, ctx);
|
|
789
|
+
break;
|
|
790
|
+
case 'class_declaration':
|
|
791
|
+
case 'abstract_class_declaration':
|
|
792
|
+
// class expressions: `return class Foo extends Bar { ... }` or `const X = class Foo { ... }`
|
|
793
|
+
case 'class':
|
|
794
|
+
handleClassDecl(node, ctx);
|
|
795
|
+
break;
|
|
796
|
+
case 'class_static_block':
|
|
797
|
+
handleStaticBlock(node, ctx.definitions);
|
|
798
|
+
break;
|
|
799
|
+
case 'field_definition':
|
|
800
|
+
case 'public_field_definition':
|
|
801
|
+
handleFieldDef(node, ctx.definitions);
|
|
802
|
+
break;
|
|
803
|
+
case 'method_definition':
|
|
804
|
+
handleMethodDef(node, ctx);
|
|
805
|
+
break;
|
|
806
|
+
case 'interface_declaration':
|
|
807
|
+
handleInterfaceDecl(node, ctx);
|
|
808
|
+
break;
|
|
809
|
+
case 'type_alias_declaration':
|
|
810
|
+
handleTypeAliasDecl(node, ctx);
|
|
811
|
+
break;
|
|
812
|
+
case 'lexical_declaration':
|
|
813
|
+
case 'variable_declaration':
|
|
814
|
+
handleVariableDecl(node, ctx);
|
|
815
|
+
break;
|
|
816
|
+
case 'enum_declaration':
|
|
817
|
+
handleEnumDecl(node, ctx);
|
|
818
|
+
break;
|
|
819
|
+
case 'decorator':
|
|
820
|
+
handleDecorator(node, ctx.calls);
|
|
821
|
+
break;
|
|
822
|
+
case 'call_expression':
|
|
823
|
+
handleCallExpr(node, ctx);
|
|
824
|
+
break;
|
|
825
|
+
case 'new_expression':
|
|
826
|
+
handleNewExpr(node, ctx);
|
|
827
|
+
break;
|
|
828
|
+
case 'import_statement':
|
|
829
|
+
handleImportStmt(node, ctx);
|
|
830
|
+
break;
|
|
831
|
+
case 'export_statement':
|
|
832
|
+
handleExportStmt(node, ctx);
|
|
833
|
+
break;
|
|
834
|
+
case 'expression_statement':
|
|
835
|
+
handleExpressionStmt(node, ctx);
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
840
|
+
walkJavaScriptNode(node.child(i)!, ctx);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// ── Walk-path per-node-type handlers ────────────────────────────────────────
|
|
845
|
+
|
|
846
|
+
function handleFunctionDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
847
|
+
const nameNode = node.childForFieldName('name');
|
|
848
|
+
if (nameNode) {
|
|
849
|
+
const fnChildren = extractParameters(node);
|
|
850
|
+
ctx.definitions.push({
|
|
851
|
+
name: nameNode.text,
|
|
852
|
+
kind: 'function',
|
|
853
|
+
line: nodeStartLine(node),
|
|
854
|
+
endLine: nodeEndLine(node),
|
|
855
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function handleClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
861
|
+
const nameNode = node.childForFieldName('name');
|
|
862
|
+
if (!nameNode) return;
|
|
863
|
+
const className = nameNode.text;
|
|
864
|
+
const startLine = nodeStartLine(node);
|
|
865
|
+
const clsChildren = extractClassProperties(node);
|
|
866
|
+
ctx.definitions.push({
|
|
867
|
+
name: className,
|
|
868
|
+
kind: 'class',
|
|
869
|
+
line: startLine,
|
|
870
|
+
endLine: nodeEndLine(node),
|
|
871
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
872
|
+
});
|
|
873
|
+
const heritage = node.childForFieldName('heritage') || findChild(node, 'class_heritage');
|
|
874
|
+
if (heritage) {
|
|
875
|
+
const superName = extractSuperclass(heritage);
|
|
876
|
+
if (superName) {
|
|
877
|
+
ctx.classes.push({ name: className, extends: superName, line: startLine });
|
|
878
|
+
}
|
|
879
|
+
const implementsList = extractImplements(heritage);
|
|
880
|
+
for (const iface of implementsList) {
|
|
881
|
+
ctx.classes.push({ name: className, implements: iface, line: startLine });
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function handleMethodDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
887
|
+
const nameNode = node.childForFieldName('name');
|
|
888
|
+
if (nameNode) {
|
|
889
|
+
let methName: string;
|
|
890
|
+
if (nameNode.type === 'computed_property_name') {
|
|
891
|
+
// Extract the inner string literal from `['methodName']` or `["methodName"]`.
|
|
892
|
+
// Non-string computed keys (e.g. `[Symbol.iterator]`) cannot be resolved at
|
|
893
|
+
// dot-notation call sites, so skip them entirely.
|
|
894
|
+
const inner = nameNode.child(1); // child(0)='[', child(1)=string, child(2)=']'
|
|
895
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) return;
|
|
896
|
+
methName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
897
|
+
if (!methName) return;
|
|
898
|
+
} else {
|
|
899
|
+
methName = nameNode.text;
|
|
900
|
+
}
|
|
901
|
+
const parentClass = findParentClass(node);
|
|
902
|
+
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
903
|
+
const methChildren = extractParameters(node);
|
|
904
|
+
const methVis = extractVisibility(node);
|
|
905
|
+
ctx.definitions.push({
|
|
906
|
+
name: fullName,
|
|
907
|
+
kind: 'method',
|
|
908
|
+
line: nodeStartLine(node),
|
|
909
|
+
endLine: nodeEndLine(node),
|
|
910
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
911
|
+
visibility: methVis,
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Create a synthetic `ClassName.<static:L:C>` definition for a class static block
|
|
918
|
+
* so that calls inside the block can be attributed to a method-kind node and
|
|
919
|
+
* `resolveThisDispatch` can walk up to the parent class for `super.method()`.
|
|
920
|
+
*
|
|
921
|
+
* The start line and column are appended to the name to ensure uniqueness when a
|
|
922
|
+
* class has multiple `static { }` blocks (each has a distinct start position even
|
|
923
|
+
* if on the same line).
|
|
924
|
+
*
|
|
925
|
+
* Tree-sitter uses `class_static_block` (not `static_block`) for `static { ... }`.
|
|
926
|
+
*/
|
|
927
|
+
function handleStaticBlock(node: TreeSitterNode, definitions: Definition[]): void {
|
|
928
|
+
const parentClass = findParentClass(node);
|
|
929
|
+
if (!parentClass) return;
|
|
930
|
+
const line = nodeStartLine(node);
|
|
931
|
+
const col = node.startPosition.column;
|
|
932
|
+
definitions.push({
|
|
933
|
+
name: `${parentClass}.<static:${line}:${col}>`,
|
|
934
|
+
kind: 'method',
|
|
935
|
+
line,
|
|
936
|
+
endLine: nodeEndLine(node),
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Emit a `ClassName.fieldName` definition for class fields that have an initializer.
|
|
942
|
+
* This lets `findCaller` attribute calls inside field initializers (e.g. static field
|
|
943
|
+
* side-effects) to the field rather than the enclosing class.
|
|
944
|
+
*
|
|
945
|
+
* JS `field_definition` uses the `'property'` field name; TS
|
|
946
|
+
* `public_field_definition` uses `'name'`. As a third fallback (Rust/TS parity) we
|
|
947
|
+
* also check for a positional `property_identifier` child.
|
|
948
|
+
*/
|
|
949
|
+
const CALLABLE_FIELD_TYPES = new Set([
|
|
950
|
+
'arrow_function',
|
|
951
|
+
'function_expression',
|
|
952
|
+
'generator_function',
|
|
953
|
+
]);
|
|
954
|
+
|
|
955
|
+
function handleFieldDef(node: TreeSitterNode, definitions: Definition[]): void {
|
|
956
|
+
// JS field_definition uses 'property' field; TS public_field_definition uses 'name' field
|
|
957
|
+
const nameNode =
|
|
958
|
+
node.childForFieldName('name') ||
|
|
959
|
+
node.childForFieldName('property') ||
|
|
960
|
+
findChild(node, 'property_identifier');
|
|
961
|
+
const valueNode = node.childForFieldName('value');
|
|
962
|
+
if (!nameNode || !valueNode) return;
|
|
963
|
+
if (nameNode.type === 'computed_property_name') return;
|
|
964
|
+
// Only emit a callable definition when the initializer is a function/arrow expression.
|
|
965
|
+
// Scalar fields like `static x = 42` should not appear as method-kind nodes.
|
|
966
|
+
if (!CALLABLE_FIELD_TYPES.has(valueNode.type)) return;
|
|
967
|
+
const fieldName = nameNode.text;
|
|
968
|
+
if (!fieldName) return;
|
|
969
|
+
const parentClass = findParentClass(node);
|
|
970
|
+
if (!parentClass) return;
|
|
971
|
+
definitions.push({
|
|
972
|
+
name: `${parentClass}.${fieldName}`,
|
|
973
|
+
kind: 'method',
|
|
974
|
+
line: nodeStartLine(node),
|
|
975
|
+
endLine: nodeEndLine(node),
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function handleInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
980
|
+
const nameNode = node.childForFieldName('name');
|
|
981
|
+
if (!nameNode) return;
|
|
982
|
+
ctx.definitions.push({
|
|
983
|
+
name: nameNode.text,
|
|
984
|
+
kind: 'interface',
|
|
985
|
+
line: nodeStartLine(node),
|
|
986
|
+
endLine: nodeEndLine(node),
|
|
987
|
+
});
|
|
988
|
+
const body =
|
|
989
|
+
node.childForFieldName('body') ||
|
|
990
|
+
findChild(node, 'interface_body') ||
|
|
991
|
+
findChild(node, 'object_type');
|
|
992
|
+
if (body) {
|
|
993
|
+
extractInterfaceMethods(body, nameNode.text, ctx.definitions);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function handleTypeAliasDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
998
|
+
const nameNode = node.childForFieldName('name');
|
|
999
|
+
if (nameNode) {
|
|
1000
|
+
ctx.definitions.push({
|
|
1001
|
+
name: nameNode.text,
|
|
1002
|
+
kind: 'type',
|
|
1003
|
+
line: nodeStartLine(node),
|
|
1004
|
+
endLine: nodeEndLine(node),
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Extract definitions from destructured object bindings.
|
|
1011
|
+
* `const { handleToken, checkPermissions } = initAuth(...)` creates definitions
|
|
1012
|
+
* for handleToken and checkPermissions so they can be resolved as call targets.
|
|
1013
|
+
*/
|
|
1014
|
+
function extractDestructuredBindings(
|
|
1015
|
+
pattern: TreeSitterNode,
|
|
1016
|
+
line: number,
|
|
1017
|
+
endLine: number,
|
|
1018
|
+
definitions: Definition[],
|
|
1019
|
+
): void {
|
|
1020
|
+
for (let i = 0; i < pattern.childCount; i++) {
|
|
1021
|
+
const child = pattern.child(i);
|
|
1022
|
+
if (!child) continue;
|
|
1023
|
+
if (
|
|
1024
|
+
child.type === 'shorthand_property_identifier_pattern' ||
|
|
1025
|
+
child.type === 'shorthand_property_identifier'
|
|
1026
|
+
) {
|
|
1027
|
+
// { handleToken } — shorthand binding
|
|
1028
|
+
definitions.push({ name: child.text, kind: 'function', line, endLine });
|
|
1029
|
+
} else if (child.type === 'pair_pattern' || child.type === 'pair') {
|
|
1030
|
+
// { original: renamed } — renamed binding, use the local alias
|
|
1031
|
+
const value = child.childForFieldName('value');
|
|
1032
|
+
if (
|
|
1033
|
+
value &&
|
|
1034
|
+
(value.type === 'identifier' || value.type === 'shorthand_property_identifier_pattern')
|
|
1035
|
+
) {
|
|
1036
|
+
definitions.push({ name: value.text, kind: 'function', line, endLine });
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1043
|
+
const isConst = node.text.startsWith('const ');
|
|
1044
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1045
|
+
const declarator = node.child(i);
|
|
1046
|
+
if (declarator && declarator.type === 'variable_declarator') {
|
|
1047
|
+
const nameN = declarator.childForFieldName('name');
|
|
1048
|
+
const valueN = declarator.childForFieldName('value');
|
|
1049
|
+
|
|
1050
|
+
if (nameN && valueN) {
|
|
1051
|
+
const valType = valueN.type;
|
|
1052
|
+
if (
|
|
1053
|
+
valType === 'arrow_function' ||
|
|
1054
|
+
valType === 'function_expression' ||
|
|
1055
|
+
valType === 'function' ||
|
|
1056
|
+
valType === 'generator_function'
|
|
1057
|
+
) {
|
|
1058
|
+
const varFnChildren = extractParameters(valueN);
|
|
1059
|
+
ctx.definitions.push({
|
|
1060
|
+
name: nameN.text,
|
|
1061
|
+
kind: 'function',
|
|
1062
|
+
line: nodeStartLine(node),
|
|
1063
|
+
endLine: nodeEndLine(valueN),
|
|
1064
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
1065
|
+
});
|
|
1066
|
+
} else if (
|
|
1067
|
+
isConst &&
|
|
1068
|
+
nameN.type === 'identifier' &&
|
|
1069
|
+
isConstantValue(valueN) &&
|
|
1070
|
+
!hasFunctionScopeAncestor(node)
|
|
1071
|
+
) {
|
|
1072
|
+
ctx.definitions.push({
|
|
1073
|
+
name: nameN.text,
|
|
1074
|
+
kind: 'constant',
|
|
1075
|
+
line: nodeStartLine(node),
|
|
1076
|
+
endLine: nodeEndLine(node),
|
|
1077
|
+
});
|
|
1078
|
+
// Phase 8.3f: extract function/arrow properties from object literals so that
|
|
1079
|
+
// this.method() calls inside Object.defineProperty accessors can resolve them.
|
|
1080
|
+
// Scope guard: hasFunctionScopeAncestor mirrors the Rust path's find_parent_of_types
|
|
1081
|
+
// check and the sibling destructured-binding branch below — skips object literals
|
|
1082
|
+
// inside function bodies to avoid polluting the global definition index with
|
|
1083
|
+
// local variable properties (e.g. `localObj.fn` from `const localObj = { fn: ... }`
|
|
1084
|
+
// inside a function).
|
|
1085
|
+
if (valueN.type === 'object') {
|
|
1086
|
+
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1087
|
+
}
|
|
1088
|
+
} else if (
|
|
1089
|
+
!isConst &&
|
|
1090
|
+
nameN.type === 'identifier' &&
|
|
1091
|
+
valueN.type === 'object' &&
|
|
1092
|
+
!hasFunctionScopeAncestor(node)
|
|
1093
|
+
) {
|
|
1094
|
+
// `let`/`var` object literals: extract qualified method definitions so that
|
|
1095
|
+
// `obj.method()` calls resolve correctly. Mirrors Rust match_js_objlit_qualified_method_defs
|
|
1096
|
+
// which emits method_definition qualified names for ALL declaration kinds and
|
|
1097
|
+
// pair+arrow/function for let/var only (const is already handled above).
|
|
1098
|
+
// Scope guard prevents local object properties from polluting the global index.
|
|
1099
|
+
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1100
|
+
} else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1101
|
+
// Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
|
|
1102
|
+
// Each destructured property becomes a function definition so it can be
|
|
1103
|
+
// resolved when passed as a callback (e.g. router.use(handleToken)).
|
|
1104
|
+
// Restricted to const to avoid creating spurious definitions for
|
|
1105
|
+
// transient let/var destructuring (e.g. let { userId } = parseRequest(req)).
|
|
1106
|
+
// Scope guard mirrors extractDestructuredBindingsWalk (query path) and
|
|
1107
|
+
// handle_var_decl (Rust path) — skips bindings inside function bodies.
|
|
1108
|
+
extractDestructuredBindings(
|
|
1109
|
+
nameN,
|
|
1110
|
+
nodeStartLine(node),
|
|
1111
|
+
nodeEndLine(node),
|
|
1112
|
+
ctx.definitions,
|
|
1113
|
+
);
|
|
1114
|
+
} else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1115
|
+
// Array destructuring: `const [x, y] = ...` — emit a single constant node
|
|
1116
|
+
// whose name is the full array pattern text (e.g. `[x, y]`), matching
|
|
1117
|
+
// native engine behaviour. Scope guard mirrors the object_pattern branch above.
|
|
1118
|
+
ctx.definitions.push({
|
|
1119
|
+
name: nameN.text,
|
|
1120
|
+
kind: 'constant',
|
|
1121
|
+
line: nodeStartLine(node),
|
|
1122
|
+
endLine: nodeEndLine(node),
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Phase 8.3f: extract function/arrow function properties from an object literal as standalone
|
|
1132
|
+
* definitions so that `this.method()` calls inside Object.defineProperty accessor functions can
|
|
1133
|
+
* resolve them via the same-file definition lookup.
|
|
1134
|
+
*
|
|
1135
|
+
* Definitions are emitted as qualified names (`obj.baz` rather than bare `baz`) to avoid
|
|
1136
|
+
* polluting the global definition index with common property names like `init`, `run`, or
|
|
1137
|
+
* `render`. The typeMap value stored by the caller also uses the qualified name so the resolver
|
|
1138
|
+
* looks up `lookup.byName('obj.baz')` rather than `lookup.byName('baz')`.
|
|
1139
|
+
*
|
|
1140
|
+
* `const obj = { baz: () => {} }` → emits Definition { name: 'obj.baz', kind: 'function' }
|
|
1141
|
+
*/
|
|
1142
|
+
function extractObjectLiteralFunctions(
|
|
1143
|
+
objNode: TreeSitterNode,
|
|
1144
|
+
varName: string,
|
|
1145
|
+
definitions: Definition[],
|
|
1146
|
+
): void {
|
|
1147
|
+
for (let i = 0; i < objNode.childCount; i++) {
|
|
1148
|
+
const child = objNode.child(i);
|
|
1149
|
+
if (!child) continue;
|
|
1150
|
+
if (child.type === 'pair') {
|
|
1151
|
+
const keyNode = child.childForFieldName('key');
|
|
1152
|
+
const valueNode = child.childForFieldName('value');
|
|
1153
|
+
if (!keyNode || !valueNode) continue;
|
|
1154
|
+
const keyName =
|
|
1155
|
+
keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
1156
|
+
if (!keyName) continue;
|
|
1157
|
+
if (
|
|
1158
|
+
valueNode.type === 'arrow_function' ||
|
|
1159
|
+
valueNode.type === 'function_expression' ||
|
|
1160
|
+
valueNode.type === 'function'
|
|
1161
|
+
) {
|
|
1162
|
+
definitions.push({
|
|
1163
|
+
name: `${varName}.${keyName}`,
|
|
1164
|
+
kind: 'function',
|
|
1165
|
+
line: nodeStartLine(child),
|
|
1166
|
+
endLine: nodeEndLine(valueNode),
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
} else if (child.type === 'method_definition') {
|
|
1170
|
+
const nameNode = child.childForFieldName('name');
|
|
1171
|
+
if (nameNode) {
|
|
1172
|
+
let methodName: string;
|
|
1173
|
+
if (nameNode.type === 'computed_property_name') {
|
|
1174
|
+
// Strip brackets+quotes from `['methodName']` to get a resolvable name.
|
|
1175
|
+
// Skip non-string computed keys (e.g. [Symbol.iterator]).
|
|
1176
|
+
const inner = nameNode.child(1);
|
|
1177
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) continue;
|
|
1178
|
+
methodName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
1179
|
+
if (!methodName) continue;
|
|
1180
|
+
} else {
|
|
1181
|
+
methodName = nameNode.text;
|
|
1182
|
+
}
|
|
1183
|
+
definitions.push({
|
|
1184
|
+
name: `${varName}.${methodName}`,
|
|
1185
|
+
kind: 'function',
|
|
1186
|
+
line: nodeStartLine(child),
|
|
1187
|
+
endLine: nodeEndLine(child),
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function handleEnumDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1195
|
+
const nameNode = node.childForFieldName('name');
|
|
1196
|
+
if (!nameNode) return;
|
|
1197
|
+
const enumChildren: SubDeclaration[] = [];
|
|
1198
|
+
const body = node.childForFieldName('body') || findChild(node, 'enum_body');
|
|
1199
|
+
if (body) {
|
|
1200
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
1201
|
+
const member = body.child(i);
|
|
1202
|
+
if (!member) continue;
|
|
1203
|
+
if (member.type === 'enum_assignment' || member.type === 'property_identifier') {
|
|
1204
|
+
const mName = member.childForFieldName('name') || member.child(0);
|
|
1205
|
+
if (mName) {
|
|
1206
|
+
enumChildren.push({
|
|
1207
|
+
name: mName.text,
|
|
1208
|
+
kind: 'constant',
|
|
1209
|
+
line: nodeStartLine(member),
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
ctx.definitions.push({
|
|
1216
|
+
name: nameNode.text,
|
|
1217
|
+
kind: 'enum',
|
|
1218
|
+
line: nodeStartLine(node),
|
|
1219
|
+
endLine: nodeEndLine(node),
|
|
1220
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1225
|
+
const fn = node.childForFieldName('function');
|
|
1226
|
+
if (!fn) return;
|
|
1227
|
+
if (fn.type === 'import') {
|
|
1228
|
+
handleDynamicImportCall(node, ctx.imports);
|
|
1229
|
+
} else {
|
|
1230
|
+
// this() calls: `this` used as a function (not as a receiver).
|
|
1231
|
+
if (fn.type === 'this') {
|
|
1232
|
+
ctx.calls.push({ name: 'this', line: nodeStartLine(node) });
|
|
1233
|
+
return; // no further processing needed for this()-style calls
|
|
1234
|
+
}
|
|
1235
|
+
const callInfo = extractCallInfo(fn, node);
|
|
1236
|
+
if (callInfo) ctx.calls.push(callInfo);
|
|
1237
|
+
if (fn.type === 'member_expression') {
|
|
1238
|
+
const cbDef = extractCallbackDefinition(node, fn);
|
|
1239
|
+
if (cbDef) ctx.definitions.push(cbDef);
|
|
1240
|
+
// this-call bindings: `fn.call(namedCtx, ...)` / `fn.apply(namedCtx, ...)`
|
|
1241
|
+
const obj = fn.childForFieldName('object');
|
|
1242
|
+
const prop = fn.childForFieldName('property');
|
|
1243
|
+
if (
|
|
1244
|
+
obj?.type === 'identifier' &&
|
|
1245
|
+
prop &&
|
|
1246
|
+
(prop.text === 'call' || prop.text === 'apply') &&
|
|
1247
|
+
!BUILTIN_GLOBALS.has(obj.text)
|
|
1248
|
+
) {
|
|
1249
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
1250
|
+
if (args) {
|
|
1251
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
1252
|
+
const child = args.child(i);
|
|
1253
|
+
if (!child) continue;
|
|
1254
|
+
const t = child.type;
|
|
1255
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
1256
|
+
if (
|
|
1257
|
+
t === 'identifier' &&
|
|
1258
|
+
!BUILTIN_GLOBALS.has(child.text) &&
|
|
1259
|
+
child.text !== 'undefined' &&
|
|
1260
|
+
child.text !== 'null'
|
|
1261
|
+
) {
|
|
1262
|
+
ctx.thisCallBindings!.push({ callee: obj.text, thisArg: child.text });
|
|
1263
|
+
}
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
ctx.calls.push(...extractCallbackReferenceCalls(node));
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function handleNewExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1274
|
+
const ctor = node.childForFieldName('constructor') || node.child(1);
|
|
1275
|
+
if (!ctor) return;
|
|
1276
|
+
if (ctor.type === 'identifier') {
|
|
1277
|
+
if (ctor.text === 'Function') {
|
|
1278
|
+
// new Function(body) — dynamic code execution; undecidable static target
|
|
1279
|
+
ctx.calls.push({
|
|
1280
|
+
name: '<dynamic:eval>',
|
|
1281
|
+
line: nodeStartLine(node),
|
|
1282
|
+
dynamic: true,
|
|
1283
|
+
dynamicKind: 'eval' as DynamicKind,
|
|
1284
|
+
});
|
|
1285
|
+
} else {
|
|
1286
|
+
ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
|
|
1287
|
+
}
|
|
1288
|
+
} else if (ctor.type === 'member_expression') {
|
|
1289
|
+
const callInfo = extractCallInfo(ctor, node);
|
|
1290
|
+
if (callInfo) ctx.calls.push(callInfo);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Handle a TypeScript/JS decorator node.
|
|
1296
|
+
*
|
|
1297
|
+
* Only handles bare-identifier and bare-member-expression decorators
|
|
1298
|
+
* (`@Foo`, `@Foo.bar`) since decorated call expressions (`@Foo()`, `@Foo.bar()`)
|
|
1299
|
+
* are already visited as `call_expression` children by the recursive walker.
|
|
1300
|
+
*/
|
|
1301
|
+
function handleDecorator(node: TreeSitterNode, calls: Call[]): void {
|
|
1302
|
+
// Decorators wrap their expression; find the first non-@ child
|
|
1303
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1304
|
+
const child = node.child(i);
|
|
1305
|
+
if (!child || child.type === '@') continue;
|
|
1306
|
+
const t = child.type;
|
|
1307
|
+
if (t === 'identifier') {
|
|
1308
|
+
// @Foo — the identifier is the decorator factory; emit as reflection call
|
|
1309
|
+
calls.push({
|
|
1310
|
+
name: child.text,
|
|
1311
|
+
line: nodeStartLine(node),
|
|
1312
|
+
dynamic: true,
|
|
1313
|
+
dynamicKind: 'reflection',
|
|
1314
|
+
});
|
|
1315
|
+
} else if (t === 'member_expression') {
|
|
1316
|
+
// @Foo.bar — emit as reflection; always mark dynamic since it's decorator dispatch
|
|
1317
|
+
const callInfo = extractCallInfo(child, node);
|
|
1318
|
+
if (callInfo) calls.push({ ...callInfo, dynamic: true, dynamicKind: 'reflection' });
|
|
1319
|
+
}
|
|
1320
|
+
// call_expression / other — handled by the recursive walker automatically
|
|
1321
|
+
break;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
1326
|
+
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
1327
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
1328
|
+
if (!args) return;
|
|
1329
|
+
const strArg = findChild(args, 'string');
|
|
1330
|
+
if (strArg) {
|
|
1331
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
1332
|
+
const names = extractDynamicImportNames(node);
|
|
1333
|
+
imports.push({ source: modPath, names, line: nodeStartLine(node), dynamicImport: true });
|
|
1334
|
+
} else {
|
|
1335
|
+
debug(
|
|
1336
|
+
`Skipping non-static dynamic import() at line ${nodeStartLine(node)} (template literal or variable)`,
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
function handleImportStmt(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1342
|
+
const isTypeOnly = node.text.startsWith('import type');
|
|
1343
|
+
const source = node.childForFieldName('source') || findChild(node, 'string');
|
|
1344
|
+
if (source) {
|
|
1345
|
+
const modPath = source.text.replace(/['"]/g, '');
|
|
1346
|
+
const names = extractImportNames(node);
|
|
1347
|
+
ctx.imports.push({
|
|
1348
|
+
source: modPath,
|
|
1349
|
+
names,
|
|
1350
|
+
line: nodeStartLine(node),
|
|
1351
|
+
typeOnly: isTypeOnly,
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function handleExportStmt(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1357
|
+
const exportLine = nodeStartLine(node);
|
|
1358
|
+
const decl = node.childForFieldName('declaration');
|
|
1359
|
+
if (decl) {
|
|
1360
|
+
const declType = decl.type;
|
|
1361
|
+
const kindMap: Record<string, string> = {
|
|
1362
|
+
function_declaration: 'function',
|
|
1363
|
+
generator_function_declaration: 'function',
|
|
1364
|
+
class_declaration: 'class',
|
|
1365
|
+
abstract_class_declaration: 'class',
|
|
1366
|
+
interface_declaration: 'interface',
|
|
1367
|
+
type_alias_declaration: 'type',
|
|
1368
|
+
};
|
|
1369
|
+
const kind = kindMap[declType];
|
|
1370
|
+
if (kind) {
|
|
1371
|
+
const n = decl.childForFieldName('name');
|
|
1372
|
+
if (n) ctx.exports.push({ name: n.text, kind: kind as Export['kind'], line: exportLine });
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
const source = node.childForFieldName('source') || findChild(node, 'string');
|
|
1376
|
+
if (source && !decl) {
|
|
1377
|
+
const modPath = source.text.replace(/['"]/g, '');
|
|
1378
|
+
const reexportNames = extractImportNames(node);
|
|
1379
|
+
const nodeText = node.text;
|
|
1380
|
+
const isWildcard = nodeText.includes('export *') || nodeText.includes('export*');
|
|
1381
|
+
ctx.imports.push({
|
|
1382
|
+
source: modPath,
|
|
1383
|
+
names: reexportNames,
|
|
1384
|
+
line: exportLine,
|
|
1385
|
+
reexport: true,
|
|
1386
|
+
wildcardReexport: isWildcard && reexportNames.length === 0,
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
function handleExpressionStmt(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
1392
|
+
const expr = node.child(0);
|
|
1393
|
+
if (expr && expr.type === 'assignment_expression') {
|
|
1394
|
+
const left = expr.childForFieldName('left');
|
|
1395
|
+
const right = expr.childForFieldName('right');
|
|
1396
|
+
if (left && right) handleCommonJSAssignment(left, right, node, ctx.imports);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
1401
|
+
|
|
1402
|
+
function extractParameters(node: TreeSitterNode): SubDeclaration[] {
|
|
1403
|
+
const params: SubDeclaration[] = [];
|
|
1404
|
+
const paramsNode = node.childForFieldName('parameters') || findChild(node, 'formal_parameters');
|
|
1405
|
+
if (!paramsNode) return params;
|
|
1406
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
1407
|
+
const child = paramsNode.child(i);
|
|
1408
|
+
if (!child) continue;
|
|
1409
|
+
const t = child.type;
|
|
1410
|
+
if (t === 'identifier') {
|
|
1411
|
+
params.push({ name: child.text, kind: 'parameter', line: nodeStartLine(child) });
|
|
1412
|
+
} else if (
|
|
1413
|
+
t === 'required_parameter' ||
|
|
1414
|
+
t === 'optional_parameter' ||
|
|
1415
|
+
t === 'assignment_pattern'
|
|
1416
|
+
) {
|
|
1417
|
+
const nameNode =
|
|
1418
|
+
child.childForFieldName('pattern') || child.childForFieldName('left') || child.child(0);
|
|
1419
|
+
if (
|
|
1420
|
+
nameNode &&
|
|
1421
|
+
(nameNode.type === 'identifier' ||
|
|
1422
|
+
nameNode.type === 'shorthand_property_identifier_pattern')
|
|
1423
|
+
) {
|
|
1424
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: nodeStartLine(child) });
|
|
1425
|
+
}
|
|
1426
|
+
} else if (t === 'rest_pattern' || t === 'rest_element') {
|
|
1427
|
+
const nameNode = child.child(1) || child.childForFieldName('name');
|
|
1428
|
+
if (nameNode && nameNode.type === 'identifier') {
|
|
1429
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: nodeStartLine(child) });
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
return params;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
function extractClassProperties(classNode: TreeSitterNode): SubDeclaration[] {
|
|
1437
|
+
const props: SubDeclaration[] = [];
|
|
1438
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
|
|
1439
|
+
if (!body) return props;
|
|
1440
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
1441
|
+
const child = body.child(i);
|
|
1442
|
+
if (!child) continue;
|
|
1443
|
+
if (
|
|
1444
|
+
child.type === 'field_definition' ||
|
|
1445
|
+
child.type === 'public_field_definition' ||
|
|
1446
|
+
child.type === 'property_definition'
|
|
1447
|
+
) {
|
|
1448
|
+
const nameNode =
|
|
1449
|
+
child.childForFieldName('name') || child.childForFieldName('property') || child.child(0);
|
|
1450
|
+
if (
|
|
1451
|
+
nameNode &&
|
|
1452
|
+
(nameNode.type === 'property_identifier' ||
|
|
1453
|
+
nameNode.type === 'identifier' ||
|
|
1454
|
+
nameNode.type === 'private_property_identifier')
|
|
1455
|
+
) {
|
|
1456
|
+
// Private # fields: nameNode.type is 'private_property_identifier'
|
|
1457
|
+
// TS modifiers: accessibility_modifier child on the field_definition
|
|
1458
|
+
const vis =
|
|
1459
|
+
nameNode.type === 'private_property_identifier' ? 'private' : extractVisibility(child);
|
|
1460
|
+
props.push({
|
|
1461
|
+
name: nameNode.text,
|
|
1462
|
+
kind: 'property',
|
|
1463
|
+
line: nodeStartLine(child),
|
|
1464
|
+
visibility: vis,
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return props;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Extract visibility modifier from a class member node.
|
|
1474
|
+
* Checks for TS access modifiers (public/private/protected) and JS private (#) fields.
|
|
1475
|
+
* Returns 'public' | 'private' | 'protected' | undefined.
|
|
1476
|
+
*/
|
|
1477
|
+
function extractVisibility(node: TreeSitterNode): 'public' | 'private' | 'protected' | undefined {
|
|
1478
|
+
// Check for TS accessibility modifiers (accessibility_modifier child)
|
|
1479
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1480
|
+
const child = node.child(i);
|
|
1481
|
+
if (!child) continue;
|
|
1482
|
+
if (child.type === 'accessibility_modifier') {
|
|
1483
|
+
const text = child.text;
|
|
1484
|
+
if (text === 'private' || text === 'protected' || text === 'public') return text;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
// Check for JS private name (# prefix) — try multiple field names
|
|
1488
|
+
const nameNode =
|
|
1489
|
+
node.childForFieldName('name') || node.childForFieldName('property') || node.child(0);
|
|
1490
|
+
if (nameNode && nameNode.type === 'private_property_identifier') {
|
|
1491
|
+
return 'private';
|
|
1492
|
+
}
|
|
1493
|
+
return undefined;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
function isConstantValue(valueNode: TreeSitterNode): boolean {
|
|
1497
|
+
if (!valueNode) return false;
|
|
1498
|
+
const t = valueNode.type;
|
|
1499
|
+
return (
|
|
1500
|
+
t === 'number' ||
|
|
1501
|
+
t === 'string' ||
|
|
1502
|
+
t === 'template_string' ||
|
|
1503
|
+
t === 'true' ||
|
|
1504
|
+
t === 'false' ||
|
|
1505
|
+
t === 'null' ||
|
|
1506
|
+
t === 'undefined' ||
|
|
1507
|
+
t === 'array' ||
|
|
1508
|
+
t === 'object' ||
|
|
1509
|
+
t === 'regex' ||
|
|
1510
|
+
t === 'unary_expression' ||
|
|
1511
|
+
t === 'binary_expression' ||
|
|
1512
|
+
t === 'new_expression'
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// ── Shared helpers ──────────────────────────────────────────────────────────
|
|
1517
|
+
|
|
1518
|
+
function extractInterfaceMethods(
|
|
1519
|
+
bodyNode: TreeSitterNode,
|
|
1520
|
+
interfaceName: string,
|
|
1521
|
+
definitions: Definition[],
|
|
1522
|
+
): void {
|
|
1523
|
+
for (let i = 0; i < bodyNode.childCount; i++) {
|
|
1524
|
+
const child = bodyNode.child(i);
|
|
1525
|
+
if (!child) continue;
|
|
1526
|
+
if (child.type === 'method_signature' || child.type === 'property_signature') {
|
|
1527
|
+
const nameNode = child.childForFieldName('name');
|
|
1528
|
+
if (nameNode) {
|
|
1529
|
+
definitions.push({
|
|
1530
|
+
name: `${interfaceName}.${nameNode.text}`,
|
|
1531
|
+
kind: 'method',
|
|
1532
|
+
line: nodeStartLine(child),
|
|
1533
|
+
endLine: nodeEndLine(child),
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
function extractImplements(heritage: TreeSitterNode): string[] {
|
|
1541
|
+
const interfaces: string[] = [];
|
|
1542
|
+
for (let i = 0; i < heritage.childCount; i++) {
|
|
1543
|
+
const child = heritage.child(i);
|
|
1544
|
+
if (!child) continue;
|
|
1545
|
+
if (child.text === 'implements') {
|
|
1546
|
+
for (let j = i + 1; j < heritage.childCount; j++) {
|
|
1547
|
+
const next = heritage.child(j);
|
|
1548
|
+
if (!next) continue;
|
|
1549
|
+
if (next.type === 'identifier') interfaces.push(next.text);
|
|
1550
|
+
else if (next.type === 'type_identifier') interfaces.push(next.text);
|
|
1551
|
+
if (next.childCount > 0) interfaces.push(...extractImplementsFromNode(next));
|
|
1552
|
+
}
|
|
1553
|
+
break;
|
|
1554
|
+
}
|
|
1555
|
+
if (child.type === 'implements_clause') {
|
|
1556
|
+
interfaces.push(...extractImplementsFromNode(child));
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
return interfaces;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
function extractImplementsFromNode(node: TreeSitterNode): string[] {
|
|
1563
|
+
const result: string[] = [];
|
|
1564
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1565
|
+
const child = node.child(i);
|
|
1566
|
+
if (!child) continue;
|
|
1567
|
+
if (child.type === 'identifier' || child.type === 'type_identifier') result.push(child.text);
|
|
1568
|
+
if (child.childCount > 0) result.push(...extractImplementsFromNode(child));
|
|
1569
|
+
}
|
|
1570
|
+
return result;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// ── Type inference helpers ───────────────────────────────────────────────
|
|
1574
|
+
|
|
1575
|
+
function extractSimpleTypeName(typeAnnotationNode: TreeSitterNode): string | null {
|
|
1576
|
+
if (!typeAnnotationNode) return null;
|
|
1577
|
+
for (let i = 0; i < typeAnnotationNode.childCount; i++) {
|
|
1578
|
+
const child = typeAnnotationNode.child(i);
|
|
1579
|
+
if (!child) continue;
|
|
1580
|
+
const t = child.type;
|
|
1581
|
+
if (t === 'type_identifier' || t === 'identifier') return child.text;
|
|
1582
|
+
if (t === 'generic_type') return child.child(0)?.text || null;
|
|
1583
|
+
if (t === 'parenthesized_type') return extractSimpleTypeName(child);
|
|
1584
|
+
// Skip union, intersection, and array types — too ambiguous
|
|
1585
|
+
}
|
|
1586
|
+
return null;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
function extractNewExprTypeName(newExprNode: TreeSitterNode): string | null {
|
|
1590
|
+
if (newExprNode?.type !== 'new_expression') return null;
|
|
1591
|
+
const ctor = newExprNode.childForFieldName('constructor') || newExprNode.child(1);
|
|
1592
|
+
if (!ctor) return null;
|
|
1593
|
+
if (ctor.type === 'identifier') return ctor.text;
|
|
1594
|
+
if (ctor.type === 'member_expression') {
|
|
1595
|
+
const prop = ctor.childForFieldName('property');
|
|
1596
|
+
return prop ? prop.text : null;
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// ── Phase 8.2: Inter-Procedural Return Type Propagation ─────────────────────
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* Walk the AST and record the return type of every function/method definition.
|
|
1605
|
+
*
|
|
1606
|
+
* Keys: plain name (e.g. "createUser") or "ClassName.methodName" for methods.
|
|
1607
|
+
* Confidence:
|
|
1608
|
+
* - 1.0: explicit TypeScript return type annotation
|
|
1609
|
+
* - 0.85: inferred from the first `return new Constructor()` in the body
|
|
1610
|
+
*/
|
|
1611
|
+
function extractReturnTypeMapWalk(
|
|
1612
|
+
rootNode: TreeSitterNode,
|
|
1613
|
+
returnTypeMap: Map<string, TypeMapEntry>,
|
|
1614
|
+
): void {
|
|
1615
|
+
function walk(node: TreeSitterNode, depth: number, currentClass: string | null): void {
|
|
1616
|
+
if (depth >= MAX_WALK_DEPTH) return;
|
|
1617
|
+
const t = node.type;
|
|
1618
|
+
|
|
1619
|
+
if (t === 'class_declaration' || t === 'abstract_class_declaration' || t === 'class') {
|
|
1620
|
+
const nameNode = node.childForFieldName('name');
|
|
1621
|
+
const className = nameNode?.text ?? null;
|
|
1622
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1623
|
+
walk(node.child(i)!, depth + 1, className);
|
|
1624
|
+
}
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
if (t === 'function_declaration' || t === 'generator_function_declaration') {
|
|
1629
|
+
const nameNode = node.childForFieldName('name');
|
|
1630
|
+
if (nameNode?.type === 'identifier' && nameNode.text !== 'constructor') {
|
|
1631
|
+
const fnName = currentClass ? `${currentClass}.${nameNode.text}` : nameNode.text;
|
|
1632
|
+
storeReturnType(node, fnName, returnTypeMap);
|
|
1633
|
+
}
|
|
1634
|
+
// Recurse into the function body with null currentClass so nested
|
|
1635
|
+
// function declarations are not stored under the enclosing class name.
|
|
1636
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1637
|
+
walk(node.child(i)!, depth + 1, null);
|
|
1638
|
+
}
|
|
1639
|
+
return;
|
|
1640
|
+
} else if (t === 'method_definition') {
|
|
1641
|
+
const nameNode = node.childForFieldName('name');
|
|
1642
|
+
if (nameNode && currentClass && nameNode.text !== 'constructor') {
|
|
1643
|
+
storeReturnType(node, `${currentClass}.${nameNode.text}`, returnTypeMap);
|
|
1644
|
+
}
|
|
1645
|
+
// Recurse into the method body with null currentClass so nested
|
|
1646
|
+
// function declarations are not stored under the enclosing class name.
|
|
1647
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1648
|
+
walk(node.child(i)!, depth + 1, null);
|
|
1649
|
+
}
|
|
1650
|
+
return;
|
|
1651
|
+
} else if (t === 'variable_declarator') {
|
|
1652
|
+
// const foo = (): ReturnType => … or const foo = function(): ReturnType { … }
|
|
1653
|
+
const nameN = node.childForFieldName('name');
|
|
1654
|
+
const valueN = node.childForFieldName('value');
|
|
1655
|
+
if (nameN?.type === 'identifier' && valueN) {
|
|
1656
|
+
const vt = valueN.type;
|
|
1657
|
+
if (
|
|
1658
|
+
vt === 'arrow_function' ||
|
|
1659
|
+
vt === 'function_expression' ||
|
|
1660
|
+
vt === 'generator_function'
|
|
1661
|
+
) {
|
|
1662
|
+
const fnName = currentClass ? `${currentClass}.${nameN.text}` : nameN.text;
|
|
1663
|
+
storeReturnType(valueN, fnName, returnTypeMap);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1669
|
+
walk(node.child(i)!, depth + 1, currentClass);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
walk(rootNode, 0, null);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/** Extract the return type of a function node and store it in the returnTypeMap. */
|
|
1676
|
+
function storeReturnType(
|
|
1677
|
+
fnNode: TreeSitterNode,
|
|
1678
|
+
fnName: string,
|
|
1679
|
+
returnTypeMap: Map<string, TypeMapEntry>,
|
|
1680
|
+
): void {
|
|
1681
|
+
const returnTypeNode = fnNode.childForFieldName('return_type');
|
|
1682
|
+
if (returnTypeNode) {
|
|
1683
|
+
const typeName = extractSimpleTypeName(returnTypeNode);
|
|
1684
|
+
if (typeName) {
|
|
1685
|
+
const existing = returnTypeMap.get(fnName);
|
|
1686
|
+
if (!existing || existing.confidence < 1.0)
|
|
1687
|
+
returnTypeMap.set(fnName, { type: typeName, confidence: 1.0 });
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
// Infer from first `return new Constructor()` in the function body
|
|
1692
|
+
const body = fnNode.childForFieldName('body');
|
|
1693
|
+
if (body) {
|
|
1694
|
+
const inferred = findReturnNewExprType(body);
|
|
1695
|
+
if (inferred) {
|
|
1696
|
+
const existing = returnTypeMap.get(fnName);
|
|
1697
|
+
if (!existing || INFERRED_RETURN_TYPE_CONFIDENCE > existing.confidence)
|
|
1698
|
+
returnTypeMap.set(fnName, { type: inferred, confidence: INFERRED_RETURN_TYPE_CONFIDENCE });
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/** Return the constructor name from the first `return new Constructor()` in a body, or null. */
|
|
1704
|
+
function findReturnNewExprType(bodyNode: TreeSitterNode): string | null {
|
|
1705
|
+
for (let i = 0; i < bodyNode.childCount; i++) {
|
|
1706
|
+
const child = bodyNode.child(i);
|
|
1707
|
+
if (child?.type !== 'return_statement') continue;
|
|
1708
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
1709
|
+
const expr = child.child(j);
|
|
1710
|
+
if (expr?.type === 'new_expression') return extractNewExprTypeName(expr);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return null;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Resolve the return type of a call_expression node using returnTypeMap.
|
|
1718
|
+
* Handles: createUser() (identifier), service.getRepo() (member), and
|
|
1719
|
+
* getService().getRepo() (chained call) up to MAX_PROPAGATION_DEPTH hops.
|
|
1720
|
+
*
|
|
1721
|
+
* `depth` tracks total chain hops consumed so far. Each call boundary — both
|
|
1722
|
+
* resolving the receiver and resolving the final return type — costs one hop.
|
|
1723
|
+
* Confidence = annotated return type confidence − 0.1 × (depth + 1).
|
|
1724
|
+
*
|
|
1725
|
+
* Examples (annotated sources → confidence 1.0):
|
|
1726
|
+
* createUser() depth=0 → 1.0 − 0.1 = 0.9 (1 hop)
|
|
1727
|
+
* svc.getUser() depth=0 → 1.0 − 0.1 = 0.9 (1 hop; receiver from typeMap)
|
|
1728
|
+
* getService().getRepo() depth=0 → inner resolved at depth=1, outer at depth+1 → 0.8 (2 hops)
|
|
1729
|
+
*/
|
|
1730
|
+
function resolveCallExprReturnType(
|
|
1731
|
+
callNode: TreeSitterNode,
|
|
1732
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
1733
|
+
returnTypeMap: Map<string, TypeMapEntry>,
|
|
1734
|
+
depth: number,
|
|
1735
|
+
): TypeMapEntry | null {
|
|
1736
|
+
if (depth >= MAX_PROPAGATION_DEPTH) return null;
|
|
1737
|
+
|
|
1738
|
+
const fn = callNode.childForFieldName('function');
|
|
1739
|
+
if (!fn) return null;
|
|
1740
|
+
|
|
1741
|
+
if (fn.type === 'identifier') {
|
|
1742
|
+
const entry = returnTypeMap.get(fn.text);
|
|
1743
|
+
if (!entry) return null;
|
|
1744
|
+
const confidence = entry.confidence - PROPAGATION_HOP_PENALTY * (depth + 1);
|
|
1745
|
+
return confidence > 0 ? { type: entry.type, confidence } : null;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
if (fn.type === 'member_expression') {
|
|
1749
|
+
const obj = fn.childForFieldName('object');
|
|
1750
|
+
const prop = fn.childForFieldName('property');
|
|
1751
|
+
if (!obj || !prop) return null;
|
|
1752
|
+
|
|
1753
|
+
let receiverType: string | null = null;
|
|
1754
|
+
// effectiveDepth tracks the depth at which THIS call's return type is charged.
|
|
1755
|
+
// When the receiver is itself a call expression (chain), we've already consumed
|
|
1756
|
+
// a hop resolving it, so charge this call at depth+1.
|
|
1757
|
+
let effectiveDepth = depth;
|
|
1758
|
+
|
|
1759
|
+
if (obj.type === 'identifier') {
|
|
1760
|
+
const typeEntry = typeMap.get(obj.text);
|
|
1761
|
+
receiverType = typeEntry ? typeEntry.type : null;
|
|
1762
|
+
} else if (obj.type === 'call_expression') {
|
|
1763
|
+
// Each link in a call chain costs an extra hop.
|
|
1764
|
+
const innerResult = resolveCallExprReturnType(obj, typeMap, returnTypeMap, depth + 1);
|
|
1765
|
+
receiverType = innerResult ? innerResult.type : null;
|
|
1766
|
+
effectiveDepth = depth + 1;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
if (receiverType) {
|
|
1770
|
+
const entry = returnTypeMap.get(`${receiverType}.${prop.text}`);
|
|
1771
|
+
if (entry) {
|
|
1772
|
+
const confidence = entry.confidence - PROPAGATION_HOP_PENALTY * (effectiveDepth + 1);
|
|
1773
|
+
return confidence > 0 ? { type: entry.type, confidence } : null;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
return null;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* Record a call assignment into callAssignments for cross-file propagation.
|
|
1783
|
+
* Only records cases where the callee is a simple identifier or a method call
|
|
1784
|
+
* on a known-typed variable — chain expressions are skipped (handled locally).
|
|
1785
|
+
*/
|
|
1786
|
+
function recordCallAssignment(
|
|
1787
|
+
callNode: TreeSitterNode,
|
|
1788
|
+
varName: string,
|
|
1789
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
1790
|
+
callAssignments: CallAssignment[],
|
|
1791
|
+
): void {
|
|
1792
|
+
const fn = callNode.childForFieldName('function');
|
|
1793
|
+
if (!fn) return;
|
|
1794
|
+
if (fn.type === 'identifier') {
|
|
1795
|
+
callAssignments.push({ varName, calleeName: fn.text });
|
|
1796
|
+
} else if (fn.type === 'member_expression') {
|
|
1797
|
+
const obj = fn.childForFieldName('object');
|
|
1798
|
+
const prop = fn.childForFieldName('property');
|
|
1799
|
+
if (obj?.type === 'identifier' && prop) {
|
|
1800
|
+
const receiverEntry = typeMap.get(obj.text);
|
|
1801
|
+
callAssignments.push({
|
|
1802
|
+
varName,
|
|
1803
|
+
calleeName: prop.text,
|
|
1804
|
+
receiverTypeName: receiverEntry?.type,
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/**
|
|
1811
|
+
* Phase 8.5 (RTA): collect all constructor names from `new X()` expressions
|
|
1812
|
+
* in the file. Captures both assigned (`const x = new Foo()`) and unassigned
|
|
1813
|
+
* (`doSomething(new Foo())`) usages that the typeMap-based approach would miss.
|
|
1814
|
+
*/
|
|
1815
|
+
// `new X()` constructor-name collection (Phase 8.5 RTA instantiation tracking)
|
|
1816
|
+
// happens inline in runCollectorWalk's new_expression case.
|
|
1817
|
+
|
|
1818
|
+
/**
|
|
1819
|
+
* Walk the AST to find `Object.defineProperty(obj, "bar", { get: getter })` patterns
|
|
1820
|
+
* and record which functions are used as getter/setter accessors for which objects.
|
|
1821
|
+
*
|
|
1822
|
+
* Result is stored in the provided map as `funcName → receiverVarName`.
|
|
1823
|
+
*/
|
|
1824
|
+
function collectDefinePropertyReceiver(node: TreeSitterNode, out: Map<string, string>): void {
|
|
1825
|
+
const fn = node.childForFieldName('function');
|
|
1826
|
+
// Match `Object.defineProperty`
|
|
1827
|
+
if (fn?.type !== 'member_expression') return;
|
|
1828
|
+
const obj = fn.childForFieldName('object');
|
|
1829
|
+
const prop = fn.childForFieldName('property');
|
|
1830
|
+
if (obj?.type !== 'identifier' || obj.text !== 'Object' || prop?.text !== 'defineProperty') {
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
const argsNode = node.childForFieldName('arguments') ?? findChild(node, 'arguments');
|
|
1834
|
+
if (!argsNode) return;
|
|
1835
|
+
// Collect non-punctuation children: arg0 (target obj), arg1 (prop name string), arg2 (descriptor)
|
|
1836
|
+
const argChildren: TreeSitterNode[] = [];
|
|
1837
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
1838
|
+
const c = argsNode.child(i);
|
|
1839
|
+
if (!c) continue;
|
|
1840
|
+
if (c.type === ',' || c.type === '(' || c.type === ')') continue;
|
|
1841
|
+
argChildren.push(c);
|
|
1842
|
+
}
|
|
1843
|
+
if (argChildren.length < 3) return;
|
|
1844
|
+
const targetObj = argChildren[0];
|
|
1845
|
+
const descriptor = argChildren[2];
|
|
1846
|
+
if (targetObj?.type !== 'identifier' || descriptor?.type !== 'object') return;
|
|
1847
|
+
const targetName = targetObj.text;
|
|
1848
|
+
// Walk the descriptor object's pair children looking for get/set
|
|
1849
|
+
for (let i = 0; i < descriptor.childCount; i++) {
|
|
1850
|
+
const pair = descriptor.child(i);
|
|
1851
|
+
if (pair?.type !== 'pair') continue;
|
|
1852
|
+
const key = pair.childForFieldName('key');
|
|
1853
|
+
const val = pair.childForFieldName('value');
|
|
1854
|
+
if (
|
|
1855
|
+
key &&
|
|
1856
|
+
(key.text === 'get' || key.text === 'set') &&
|
|
1857
|
+
val?.type === 'identifier' &&
|
|
1858
|
+
!BUILTIN_GLOBALS.has(val.text)
|
|
1859
|
+
) {
|
|
1860
|
+
// Known limitation: if the same function is registered as an
|
|
1861
|
+
// accessor on multiple objects, last-write-wins — only the
|
|
1862
|
+
// last target object is retained. This is an unusual pattern
|
|
1863
|
+
// (sharing one function across multiple defineProperty calls)
|
|
1864
|
+
// and covering it would require Map<string, string[]> which
|
|
1865
|
+
// changes the consumer API. Tracked as a known edge case.
|
|
1866
|
+
out.set(val.text, targetName);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
/** Outputs for {@link runContextCollectorWalk}. */
|
|
1872
|
+
interface ContextCollectorOutputs {
|
|
1873
|
+
typeMap: Map<string, TypeMapEntry>;
|
|
1874
|
+
returnTypeMap?: Map<string, TypeMapEntry>;
|
|
1875
|
+
callAssignments?: CallAssignment[];
|
|
1876
|
+
fnRefBindings: FnRefBinding[];
|
|
1877
|
+
objectRestParamBindings: ObjectRestParamBinding[];
|
|
1878
|
+
spreadArgBindings: SpreadArgBinding[];
|
|
1879
|
+
forOfBindings: ForOfBinding[];
|
|
1880
|
+
arrayCallbackBindings: ArrayCallbackBinding[];
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
/**
|
|
1884
|
+
* Single context-tracking pass combining what were three separate full-tree
|
|
1885
|
+
* walks (typeMap, object-rest params, spread/for-of) — see runCollectorWalk
|
|
1886
|
+
* for why traversal count dominates extraction cost on WASM trees.
|
|
1887
|
+
*
|
|
1888
|
+
* Each concern keeps its own enclosing-class register because their reset
|
|
1889
|
+
* rules intentionally differ:
|
|
1890
|
+
*
|
|
1891
|
+
* - typeMap (`typeMapClass`): extracts variable-to-type assignments.
|
|
1892
|
+
* Values are `{ type: string, confidence: number }`:
|
|
1893
|
+
* - 1.0: explicit constructor (`new Foo()`)
|
|
1894
|
+
* - 0.9: type annotation (`: Foo`) or typed parameter
|
|
1895
|
+
* - 0.85: property write (`obj.prop = fn` — Phase 8.3d pts tracking)
|
|
1896
|
+
* - 0.7–0.9: inter-procedural propagation from return-type map (Phase 8.2)
|
|
1897
|
+
* - 0.7: factory method call (`Foo.create()` — uppercase-first heuristic)
|
|
1898
|
+
* Higher-confidence entries take priority when the same variable is seen
|
|
1899
|
+
* twice. Class declarations propagate their name into the subtree; class
|
|
1900
|
+
* *expressions* (`const Foo = class Bar { … }`) propagate null because the
|
|
1901
|
+
* expression-internal name is never visible to the resolver, preserving the
|
|
1902
|
+
* `this.prop` fallback in resolveByMethodOrGlobal. No reset at function
|
|
1903
|
+
* boundaries.
|
|
1904
|
+
*
|
|
1905
|
+
* - object-rest params (`objectRestClass`, Phase 8.3f): context flows only
|
|
1906
|
+
* class_declaration/class → class_body → method_definition so methods are
|
|
1907
|
+
* keyed "ClassName.method"; every other node type resets to null, and
|
|
1908
|
+
* function/method bodies recurse with null so nested declarations don't
|
|
1909
|
+
* inherit the class context.
|
|
1910
|
+
*
|
|
1911
|
+
* - spread/for-of (`funcStack`/`classStack`, Phase 8.3e): tracks the
|
|
1912
|
+
* enclosing *function* (not just class) via push/pop so for-of bindings
|
|
1913
|
+
* record the qualified enclosing callable (e.g. 'Foo.bar', 'obj.method',
|
|
1914
|
+
* or '<module>' at top level).
|
|
1915
|
+
*
|
|
1916
|
+
* NOTE: returnTypeMap population stays a separate, earlier pass
|
|
1917
|
+
* (extractReturnTypeMapWalk) — handleVarDeclaratorTypeMap reads it for
|
|
1918
|
+
* inter-procedural propagation, so it must be complete for the whole file
|
|
1919
|
+
* before any declarator is processed (a function declared *after* its first
|
|
1920
|
+
* use would otherwise be missed).
|
|
1921
|
+
*/
|
|
1922
|
+
function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollectorOutputs): void {
|
|
1923
|
+
const funcStack: string[] = [];
|
|
1924
|
+
const classStack: string[] = [];
|
|
1925
|
+
|
|
1926
|
+
const walk = (
|
|
1927
|
+
node: TreeSitterNode,
|
|
1928
|
+
depth: number,
|
|
1929
|
+
typeMapClass: string | null,
|
|
1930
|
+
objectRestClass: string | null,
|
|
1931
|
+
): void => {
|
|
1932
|
+
if (depth >= MAX_WALK_DEPTH) return;
|
|
1933
|
+
const t = node.type;
|
|
1934
|
+
|
|
1935
|
+
const isClassDecl = t === 'class_declaration' || t === 'abstract_class_declaration';
|
|
1936
|
+
const isClassExpr = t === 'class';
|
|
1937
|
+
const isFnDecl = t === 'function_declaration' || t === 'generator_function_declaration';
|
|
1938
|
+
|
|
1939
|
+
// Class name read once, shared by every concern that needs it below.
|
|
1940
|
+
let className: string | null = null;
|
|
1941
|
+
let classNameIsIdentifier = false;
|
|
1942
|
+
if (isClassDecl || isClassExpr) {
|
|
1943
|
+
const nameNode = node.childForFieldName('name');
|
|
1944
|
+
className = nameNode?.text ?? null;
|
|
1945
|
+
classNameIsIdentifier = nameNode?.type === 'identifier';
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// ── spread/for-of enclosing-context stacks (push on enter, pop after children) ──
|
|
1949
|
+
let pushedFunc = false;
|
|
1950
|
+
let pushedClass = false;
|
|
1951
|
+
if (isClassDecl || isClassExpr) {
|
|
1952
|
+
// The stack push keeps the original walk's `identifier`-only check (TS
|
|
1953
|
+
// class names parse as type_identifier and were never pushed), while
|
|
1954
|
+
// typeMapClass/objectRestClass below use the bare text like their
|
|
1955
|
+
// original walks did.
|
|
1956
|
+
if (className && classNameIsIdentifier) {
|
|
1957
|
+
classStack.push(className);
|
|
1958
|
+
pushedClass = true;
|
|
1959
|
+
}
|
|
1960
|
+
} else if (isFnDecl) {
|
|
1961
|
+
const nameNode = node.childForFieldName('name');
|
|
1962
|
+
if (nameNode?.type === 'identifier') {
|
|
1963
|
+
funcStack.push(nameNode.text);
|
|
1964
|
+
pushedFunc = true;
|
|
1965
|
+
}
|
|
1966
|
+
} else if (t === 'method_definition') {
|
|
1967
|
+
const nameNode = node.childForFieldName('name');
|
|
1968
|
+
if (nameNode) {
|
|
1969
|
+
// Qualify with the enclosing class name so the PTS key matches
|
|
1970
|
+
// callerName from findCaller (which uses def.name = 'ClassName.method').
|
|
1971
|
+
const enclosingClass = classStack.length > 0 ? classStack[classStack.length - 1] : null;
|
|
1972
|
+
let rawName: string;
|
|
1973
|
+
if (nameNode.type === 'computed_property_name') {
|
|
1974
|
+
const inner = nameNode.child(1);
|
|
1975
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) {
|
|
1976
|
+
// Non-string computed key — skip adding to funcStack (no resolvable name).
|
|
1977
|
+
rawName = '';
|
|
1978
|
+
} else {
|
|
1979
|
+
rawName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
1980
|
+
}
|
|
1981
|
+
} else {
|
|
1982
|
+
rawName = nameNode.text;
|
|
1983
|
+
}
|
|
1984
|
+
if (rawName) {
|
|
1985
|
+
const qualifiedName = enclosingClass ? `${enclosingClass}.${rawName}` : rawName;
|
|
1986
|
+
funcStack.push(qualifiedName);
|
|
1987
|
+
pushedFunc = true;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
} else if (t === 'variable_declarator') {
|
|
1991
|
+
// `const process = (arr) => { ... }` — arrow/expression functions assigned
|
|
1992
|
+
// to a variable have no `name` field on the function node itself.
|
|
1993
|
+
const nameNode = node.childForFieldName('name');
|
|
1994
|
+
const valueNode = node.childForFieldName('value');
|
|
1995
|
+
if (
|
|
1996
|
+
nameNode?.type === 'identifier' &&
|
|
1997
|
+
(valueNode?.type === 'arrow_function' || valueNode?.type === 'function_expression')
|
|
1998
|
+
) {
|
|
1999
|
+
funcStack.push(nameNode.text);
|
|
2000
|
+
pushedFunc = true;
|
|
2001
|
+
}
|
|
2002
|
+
} else if (t === 'assignment_expression') {
|
|
2003
|
+
// `obj.method = function() { ... }` — func-prop assignment.
|
|
2004
|
+
// Mirror handleFuncPropAssignment's logic so for-of loops inside the
|
|
2005
|
+
// body get the correct enclosingFunc (e.g. 'obj.method') instead of
|
|
2006
|
+
// '<module>' or the wrong outer function name.
|
|
2007
|
+
const lhs = node.childForFieldName('left');
|
|
2008
|
+
const rhs = node.childForFieldName('right');
|
|
2009
|
+
if (
|
|
2010
|
+
lhs?.type === 'member_expression' &&
|
|
2011
|
+
(rhs?.type === 'function_expression' || rhs?.type === 'arrow_function')
|
|
2012
|
+
) {
|
|
2013
|
+
const obj = lhs.childForFieldName('object');
|
|
2014
|
+
const prop = lhs.childForFieldName('property');
|
|
2015
|
+
if (
|
|
2016
|
+
obj?.type === 'identifier' &&
|
|
2017
|
+
(prop?.type === 'property_identifier' || prop?.type === 'identifier') &&
|
|
2018
|
+
!BUILTIN_GLOBALS.has(obj.text) &&
|
|
2019
|
+
prop.text !== 'prototype'
|
|
2020
|
+
) {
|
|
2021
|
+
funcStack.push(`${obj.text}.${prop.text}`);
|
|
2022
|
+
pushedFunc = true;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// ── per-node collectors (class nodes match none of these types) ──
|
|
2028
|
+
if (t === 'variable_declarator') {
|
|
2029
|
+
handleVarDeclaratorTypeMap(
|
|
2030
|
+
node,
|
|
2031
|
+
out.typeMap,
|
|
2032
|
+
out.returnTypeMap,
|
|
2033
|
+
out.callAssignments,
|
|
2034
|
+
out.fnRefBindings,
|
|
2035
|
+
);
|
|
2036
|
+
collectCollectionWrapBinding(node, out.fnRefBindings);
|
|
2037
|
+
} else if (t === 'required_parameter' || t === 'optional_parameter') {
|
|
2038
|
+
handleParamTypeMap(node, out.typeMap);
|
|
2039
|
+
} else if (t === 'public_field_definition' || t === 'field_definition') {
|
|
2040
|
+
handleFieldDefTypeMap(node, out.typeMap, typeMapClass);
|
|
2041
|
+
} else if (t === 'assignment_expression') {
|
|
2042
|
+
handlePropWriteTypeMap(node, out.typeMap, typeMapClass);
|
|
2043
|
+
} else if (t === 'call_expression') {
|
|
2044
|
+
handleDefinePropertyTypeMap(node, out.typeMap);
|
|
2045
|
+
collectSpreadAndArrayFromBindings(node, out.spreadArgBindings, out.arrayCallbackBindings);
|
|
2046
|
+
} else if (t === 'for_in_statement') {
|
|
2047
|
+
const enclosingFunc = funcStack.length > 0 ? funcStack[funcStack.length - 1]! : '<module>';
|
|
2048
|
+
collectForOfBinding(node, enclosingFunc, out.forOfBindings);
|
|
2049
|
+
}
|
|
2050
|
+
collectObjectRestParams(node, t, objectRestClass, out.objectRestParamBindings);
|
|
2051
|
+
|
|
2052
|
+
// ── child context per concern ──
|
|
2053
|
+
const childTypeMapClass = isClassDecl ? className : isClassExpr ? null : typeMapClass;
|
|
2054
|
+
let childObjectRestClass: string | null = null;
|
|
2055
|
+
if (t === 'class_declaration' || t === 'class') {
|
|
2056
|
+
childObjectRestClass = className;
|
|
2057
|
+
} else if (t === 'class_body') {
|
|
2058
|
+
childObjectRestClass = objectRestClass;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2062
|
+
walk(node.child(i)!, depth + 1, childTypeMapClass, childObjectRestClass);
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
if (pushedFunc) funcStack.pop();
|
|
2066
|
+
if (pushedClass) classStack.pop();
|
|
2067
|
+
};
|
|
2068
|
+
|
|
2069
|
+
walk(rootNode, 0, null, null);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
/**
|
|
2073
|
+
* Record function-reference bindings from a variable_declarator's value node.
|
|
2074
|
+
*
|
|
2075
|
+
* Captures three patterns (Phase 8.3):
|
|
2076
|
+
* - `const fn = handler` (identifier alias)
|
|
2077
|
+
* - `const fn = obj.method` (member_expression alias)
|
|
2078
|
+
* - `const f = fn.bind(ctx)` (bind creates a bound alias)
|
|
2079
|
+
*
|
|
2080
|
+
* Must be called before any type-analysis early returns so every declarator
|
|
2081
|
+
* contributes to fnRefBindings regardless of whether it has a type annotation.
|
|
2082
|
+
*/
|
|
2083
|
+
function collectFnRefBindings(
|
|
2084
|
+
lhsName: string,
|
|
2085
|
+
valueN: TreeSitterNode,
|
|
2086
|
+
fnRefBindings: FnRefBinding[],
|
|
2087
|
+
): void {
|
|
2088
|
+
if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
|
|
2089
|
+
fnRefBindings.push({ lhs: lhsName, rhs: valueN.text });
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
if (valueN.type === 'member_expression') {
|
|
2093
|
+
const prop = valueN.childForFieldName('property');
|
|
2094
|
+
const obj = valueN.childForFieldName('object');
|
|
2095
|
+
// Guard: only static property access (property_identifier or identifier), not
|
|
2096
|
+
// computed subscript expressions like obj[expr] where prop.text would be the
|
|
2097
|
+
// full expression rather than a simple name — those can never match pts keys.
|
|
2098
|
+
if (
|
|
2099
|
+
prop &&
|
|
2100
|
+
(prop.type === 'property_identifier' || prop.type === 'identifier') &&
|
|
2101
|
+
obj?.type === 'identifier' &&
|
|
2102
|
+
!BUILTIN_GLOBALS.has(obj.text)
|
|
2103
|
+
) {
|
|
2104
|
+
fnRefBindings.push({ lhs: lhsName, rhs: prop.text, rhsReceiver: obj.text });
|
|
2105
|
+
}
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if (valueN.type === 'call_expression') {
|
|
2109
|
+
// `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
|
|
2110
|
+
// pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
|
|
2111
|
+
// Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
|
|
2112
|
+
// binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
|
|
2113
|
+
const callFn = valueN.childForFieldName('function');
|
|
2114
|
+
if (callFn?.type === 'member_expression') {
|
|
2115
|
+
const bindProp = callFn.childForFieldName('property');
|
|
2116
|
+
if (bindProp?.text === 'bind') {
|
|
2117
|
+
const boundFn = callFn.childForFieldName('object');
|
|
2118
|
+
if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
|
|
2119
|
+
fnRefBindings.push({ lhs: lhsName, rhs: boundFn.text });
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* Handle the `call_expression` branch of variable_declarator type-map seeding.
|
|
2128
|
+
*
|
|
2129
|
+
* Processes three sub-cases in priority order:
|
|
2130
|
+
* 1. Object.create({ ... }) — seeds composite pts keys from the prototype object (Phase 8.3e)
|
|
2131
|
+
* 2. Inter-procedural return-type propagation via returnTypeMap (Phase 8.2)
|
|
2132
|
+
* 3. Factory method heuristic: `const x = Foo.create()` → type Foo at confidence 0.7
|
|
2133
|
+
*/
|
|
2134
|
+
function handleCallExprTypeMap(
|
|
2135
|
+
lhsName: string,
|
|
2136
|
+
valueN: TreeSitterNode,
|
|
2137
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2138
|
+
returnTypeMap: Map<string, TypeMapEntry> | undefined,
|
|
2139
|
+
callAssignments: CallAssignment[] | undefined,
|
|
2140
|
+
): void {
|
|
2141
|
+
const createFn = valueN.childForFieldName('function');
|
|
2142
|
+
// Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
|
|
2143
|
+
if (createFn?.type === 'member_expression') {
|
|
2144
|
+
const createObj = createFn.childForFieldName('object');
|
|
2145
|
+
const createProp = createFn.childForFieldName('property');
|
|
2146
|
+
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
2147
|
+
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
2148
|
+
if (createArgs) {
|
|
2149
|
+
let proto: TreeSitterNode | null = null;
|
|
2150
|
+
for (let i = 0; i < createArgs.childCount; i++) {
|
|
2151
|
+
const n = createArgs.child(i);
|
|
2152
|
+
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
2153
|
+
proto = n;
|
|
2154
|
+
break;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
if (proto?.type === 'object') {
|
|
2158
|
+
seedProtoProperties(lhsName, proto, typeMap);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
2165
|
+
// the local returnTypeMap before falling back to factory heuristics.
|
|
2166
|
+
if (returnTypeMap) {
|
|
2167
|
+
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
2168
|
+
if (result) {
|
|
2169
|
+
setTypeMapEntry(typeMap, lhsName, result.type, result.confidence);
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
2174
|
+
if (callAssignments) {
|
|
2175
|
+
recordCallAssignment(valueN, lhsName, typeMap, callAssignments);
|
|
2176
|
+
}
|
|
2177
|
+
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
2178
|
+
if (createFn?.type === 'member_expression') {
|
|
2179
|
+
const obj = createFn.childForFieldName('object');
|
|
2180
|
+
if (obj?.type === 'identifier') {
|
|
2181
|
+
const objName = obj.text;
|
|
2182
|
+
if (objName[0] && objName[0] !== objName[0].toLowerCase() && !BUILTIN_GLOBALS.has(objName)) {
|
|
2183
|
+
setTypeMapEntry(typeMap, lhsName, objName, 0.7);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
/**
|
|
2190
|
+
* Seed composite pts keys from a module-level object literal assignment (Phase 8.3f).
|
|
2191
|
+
*
|
|
2192
|
+
* `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2193
|
+
* `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
2194
|
+
* `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
2195
|
+
* `const obj = { baz() {} }` (method shorthand) → typeMap['obj.baz'] = 'obj.baz'
|
|
2196
|
+
*
|
|
2197
|
+
* For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
2198
|
+
* extractObjectLiteralFunctions registers definitions under that qualified name to avoid
|
|
2199
|
+
* polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
2200
|
+
* Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
2201
|
+
* resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
2202
|
+
*
|
|
2203
|
+
* Scope guard: caller must ensure `node` is not inside a function body
|
|
2204
|
+
* (mirrors Rust handle_var_decl's find_parent_of_types check — function-scoped
|
|
2205
|
+
* `const localObj = { fn: ... }` must not shadow a module-level `const obj`).
|
|
2206
|
+
*/
|
|
2207
|
+
function handleObjectLiteralTypeMap(
|
|
2208
|
+
lhsName: string,
|
|
2209
|
+
valueN: TreeSitterNode,
|
|
2210
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2211
|
+
): void {
|
|
2212
|
+
for (let i = 0; i < valueN.childCount; i++) {
|
|
2213
|
+
const child = valueN.child(i);
|
|
2214
|
+
if (!child) continue;
|
|
2215
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
2216
|
+
setTypeMapEntry(typeMap, `${lhsName}.${child.text}`, child.text, 0.85);
|
|
2217
|
+
} else if (child.type === 'pair') {
|
|
2218
|
+
const keyNode = child.childForFieldName('key');
|
|
2219
|
+
const valNode = child.childForFieldName('value');
|
|
2220
|
+
if (!keyNode || !valNode) continue;
|
|
2221
|
+
const keyName =
|
|
2222
|
+
keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
2223
|
+
if (!keyName) continue;
|
|
2224
|
+
const qualifiedKey = `${lhsName}.${keyName}`;
|
|
2225
|
+
if (
|
|
2226
|
+
valNode.type === 'arrow_function' ||
|
|
2227
|
+
valNode.type === 'function_expression' ||
|
|
2228
|
+
valNode.type === 'function'
|
|
2229
|
+
) {
|
|
2230
|
+
// Store the qualified name so the resolver finds the qualified definition.
|
|
2231
|
+
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2232
|
+
} else if (valNode.type === 'identifier') {
|
|
2233
|
+
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
2234
|
+
}
|
|
2235
|
+
} else if (child.type === 'method_definition') {
|
|
2236
|
+
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2237
|
+
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
2238
|
+
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
2239
|
+
const nameNode = child.childForFieldName('name');
|
|
2240
|
+
if (!nameNode) continue;
|
|
2241
|
+
setTypeMapEntry(typeMap, `${lhsName}.${nameNode.text}`, `${lhsName}.${nameNode.text}`, 0.85);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* Extract type info from a variable_declarator: type annotation, constructor, or factory.
|
|
2248
|
+
*
|
|
2249
|
+
* Orchestrates four concerns in priority order:
|
|
2250
|
+
* 1. fnRefBindings — always collected first (before any early return)
|
|
2251
|
+
* 2. new_expression — constructor wins over annotation (runtime type is authoritative)
|
|
2252
|
+
* 3. type_annotation — confidence 0.9 for static analysis
|
|
2253
|
+
* 4. call_expression / object literal — delegated to handleCallExprTypeMap /
|
|
2254
|
+
* handleObjectLiteralTypeMap
|
|
2255
|
+
*/
|
|
2256
|
+
function handleVarDeclaratorTypeMap(
|
|
2257
|
+
node: TreeSitterNode,
|
|
2258
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2259
|
+
returnTypeMap?: Map<string, TypeMapEntry>,
|
|
2260
|
+
callAssignments?: CallAssignment[],
|
|
2261
|
+
fnRefBindings?: FnRefBinding[],
|
|
2262
|
+
): void {
|
|
2263
|
+
const nameN = node.childForFieldName('name');
|
|
2264
|
+
if (nameN?.type !== 'identifier') return;
|
|
2265
|
+
|
|
2266
|
+
const typeAnno = findChild(node, 'type_annotation');
|
|
2267
|
+
const valueN = node.childForFieldName('value');
|
|
2268
|
+
|
|
2269
|
+
// 1. fnRefBindings — must run before any early return so every declarator contributes.
|
|
2270
|
+
if (fnRefBindings && valueN) {
|
|
2271
|
+
collectFnRefBindings(nameN.text, valueN, fnRefBindings);
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// 2. Constructor wins over annotation: `const x: Base = new Derived()` resolves to Derived.
|
|
2275
|
+
if (valueN?.type === 'new_expression') {
|
|
2276
|
+
const ctorType = extractNewExprTypeName(valueN);
|
|
2277
|
+
if (ctorType) {
|
|
2278
|
+
setTypeMapEntry(typeMap, nameN.text, ctorType, 1.0);
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// 3. Type annotation — confidence 0.9.
|
|
2284
|
+
if (typeAnno) {
|
|
2285
|
+
const typeName = extractSimpleTypeName(typeAnno);
|
|
2286
|
+
if (typeName) {
|
|
2287
|
+
setTypeMapEntry(typeMap, nameN.text, typeName, 0.9);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
if (!valueN) return;
|
|
2293
|
+
if (valueN.type === 'new_expression') return;
|
|
2294
|
+
|
|
2295
|
+
// 4a. call_expression — Object.create / return-type propagation / factory heuristic.
|
|
2296
|
+
if (valueN.type === 'call_expression') {
|
|
2297
|
+
handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// 4b. Object literal — seed composite pts keys for module-level const objects.
|
|
2302
|
+
if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
|
|
2303
|
+
handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
/** Extract type info from a required_parameter or optional_parameter. */
|
|
2308
|
+
function handleParamTypeMap(node: TreeSitterNode, typeMap: Map<string, TypeMapEntry>): void {
|
|
2309
|
+
const nameNode =
|
|
2310
|
+
node.childForFieldName('pattern') || node.childForFieldName('left') || node.child(0);
|
|
2311
|
+
if (nameNode?.type !== 'identifier') return;
|
|
2312
|
+
const typeAnno = findChild(node, 'type_annotation');
|
|
2313
|
+
if (typeAnno) {
|
|
2314
|
+
const typeName = extractSimpleTypeName(typeAnno);
|
|
2315
|
+
if (typeName) setTypeMapEntry(typeMap, nameNode.text, typeName, 0.9);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
/**
|
|
2320
|
+
* Extract type info from a class field declaration: `private repo: Repository<User>`.
|
|
2321
|
+
*
|
|
2322
|
+
* Seeds a class-scoped key `ClassName.field` (confidence 0.9) as the primary entry
|
|
2323
|
+
* so that two classes with identically-named fields don't overwrite each other's
|
|
2324
|
+
* typeMap entry (issue #1458). The resolver's `CallerClass.X` fallback (call-resolver.ts
|
|
2325
|
+
* line 110) looks up exactly this key.
|
|
2326
|
+
*
|
|
2327
|
+
* Bare `field` and `this.field` keys are kept at lower confidence (0.6) as fallbacks
|
|
2328
|
+
* for single-class files where the resolver may not have a callerClass context.
|
|
2329
|
+
*
|
|
2330
|
+
* Mirrors the field_definition branch of match_js_type_map in
|
|
2331
|
+
* crates/codegraph-core/src/extractors/javascript.rs.
|
|
2332
|
+
*/
|
|
2333
|
+
function handleFieldDefTypeMap(
|
|
2334
|
+
node: TreeSitterNode,
|
|
2335
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2336
|
+
currentClass: string | null,
|
|
2337
|
+
): void {
|
|
2338
|
+
const nameNode =
|
|
2339
|
+
node.childForFieldName('name') ||
|
|
2340
|
+
node.childForFieldName('property') ||
|
|
2341
|
+
findChild(node, 'property_identifier');
|
|
2342
|
+
if (!nameNode) return;
|
|
2343
|
+
const kind = nameNode.type;
|
|
2344
|
+
if (
|
|
2345
|
+
kind !== 'property_identifier' &&
|
|
2346
|
+
kind !== 'identifier' &&
|
|
2347
|
+
kind !== 'private_property_identifier'
|
|
2348
|
+
)
|
|
2349
|
+
return;
|
|
2350
|
+
const typeAnno = findChild(node, 'type_annotation');
|
|
2351
|
+
if (!typeAnno) return;
|
|
2352
|
+
const typeName = extractSimpleTypeName(typeAnno);
|
|
2353
|
+
if (!typeName) return;
|
|
2354
|
+
if (currentClass) {
|
|
2355
|
+
// Primary: class-scoped key prevents cross-class collision (issue #1458).
|
|
2356
|
+
setTypeMapEntry(typeMap, `${currentClass}.${nameNode.text}`, typeName, 0.9);
|
|
2357
|
+
// Fallback: bare keys at lower confidence for single-class files or when
|
|
2358
|
+
// the resolver does not have a callerClass in scope.
|
|
2359
|
+
setTypeMapEntry(typeMap, nameNode.text, typeName, 0.6);
|
|
2360
|
+
setTypeMapEntry(typeMap, `this.${nameNode.text}`, typeName, 0.6);
|
|
2361
|
+
} else {
|
|
2362
|
+
// No enclosing class declaration (e.g. class expression) — use bare keys only.
|
|
2363
|
+
setTypeMapEntry(typeMap, nameNode.text, typeName, 0.9);
|
|
2364
|
+
setTypeMapEntry(typeMap, `this.${nameNode.text}`, typeName, 0.9);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
/**
|
|
2369
|
+
* Phase 8.3d: seed the pts map from object property writes.
|
|
2370
|
+
*
|
|
2371
|
+
* `handlers.auth = authMiddleware` → typeMap.set('handlers.auth', { type: 'authMiddleware', confidence: 0.85 })
|
|
2372
|
+
* `this.logger = new Logger(...)` → typeMap.set('UserService.logger', { type: 'Logger', confidence: 1.0 })
|
|
2373
|
+
* (keyed as ClassName.prop when currentClass is known, to avoid collisions across classes)
|
|
2374
|
+
*
|
|
2375
|
+
* Only simple `obj.prop = identifier` and `this.prop = new Ctor()` writes are tracked
|
|
2376
|
+
* (not chained `a.b.c = x`). BUILTIN_GLOBALS are skipped (e.g. `console.log = fn`).
|
|
2377
|
+
*/
|
|
2378
|
+
function handlePropWriteTypeMap(
|
|
2379
|
+
node: TreeSitterNode,
|
|
2380
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2381
|
+
currentClass: string | null,
|
|
2382
|
+
): void {
|
|
2383
|
+
const lhsN = node.childForFieldName('left');
|
|
2384
|
+
const rhsN = node.childForFieldName('right');
|
|
2385
|
+
if (!lhsN || !rhsN) return;
|
|
2386
|
+
if (lhsN.type !== 'member_expression') return;
|
|
2387
|
+
|
|
2388
|
+
const obj = lhsN.childForFieldName('object');
|
|
2389
|
+
const prop = lhsN.childForFieldName('property');
|
|
2390
|
+
if (!obj || !prop) return;
|
|
2391
|
+
// Guard: only static property access (property_identifier or identifier), not
|
|
2392
|
+
// computed subscript expressions — consistent with the adjacent fnRefBindings block.
|
|
2393
|
+
if (prop.type !== 'property_identifier' && prop.type !== 'identifier') return;
|
|
2394
|
+
|
|
2395
|
+
// this.prop = new ClassName(...) — constructor-assigned property type.
|
|
2396
|
+
// Key as ClassName.prop (class-scoped) so two classes with identically-named
|
|
2397
|
+
// properties don't overwrite each other's typeMap entry.
|
|
2398
|
+
if (obj.type === 'this' && rhsN.type === 'new_expression') {
|
|
2399
|
+
const ctorType = extractNewExprTypeName(rhsN);
|
|
2400
|
+
if (ctorType) {
|
|
2401
|
+
const key = currentClass ? `${currentClass}.${prop.text}` : `this.${prop.text}`;
|
|
2402
|
+
setTypeMapEntry(typeMap, key, ctorType, 1.0);
|
|
2403
|
+
}
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// obj.prop = identifier — existing behaviour (skip chained a.b.c = x and builtins)
|
|
2408
|
+
if (rhsN.type !== 'identifier') return;
|
|
2409
|
+
if (obj.type !== 'identifier') return;
|
|
2410
|
+
const objName = obj.text;
|
|
2411
|
+
if (BUILTIN_GLOBALS.has(objName)) return;
|
|
2412
|
+
setTypeMapEntry(typeMap, `${objName}.${prop.text}`, rhsN.text, 0.85);
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
/**
|
|
2416
|
+
* Phase 8.3e/8.3f: seed composite pts keys from Object.defineProperty / defineProperties.
|
|
2417
|
+
*
|
|
2418
|
+
* `Object.defineProperty(obj, "key", { value: fn })` → typeMap.set('obj.key', fn, 0.85)
|
|
2419
|
+
* `Object.defineProperties(obj, { "k1": { value: v1 } })` → typeMap.set('obj.k1', v1, 0.85)
|
|
2420
|
+
* `Object.defineProperty(obj, "key", { get: getter })` → typeMap.set('getter:this', obj, 0.85)
|
|
2421
|
+
*/
|
|
2422
|
+
function handleDefinePropertyTypeMap(
|
|
2423
|
+
node: TreeSitterNode,
|
|
2424
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2425
|
+
): void {
|
|
2426
|
+
const fn = node.childForFieldName('function');
|
|
2427
|
+
if (fn?.type !== 'member_expression') return;
|
|
2428
|
+
const fnObj = fn.childForFieldName('object');
|
|
2429
|
+
const fnProp = fn.childForFieldName('property');
|
|
2430
|
+
if (fnObj?.text !== 'Object') return;
|
|
2431
|
+
const method = fnProp?.text;
|
|
2432
|
+
if (method !== 'defineProperty' && method !== 'defineProperties') return;
|
|
2433
|
+
|
|
2434
|
+
const argsNode = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
2435
|
+
if (!argsNode) return;
|
|
2436
|
+
|
|
2437
|
+
const args: TreeSitterNode[] = [];
|
|
2438
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
2439
|
+
const n = argsNode.child(i);
|
|
2440
|
+
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') args.push(n);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
if (method === 'defineProperty') {
|
|
2444
|
+
if (args.length < 3) return;
|
|
2445
|
+
const arg0 = args[0]!,
|
|
2446
|
+
arg1 = args[1]!,
|
|
2447
|
+
arg2 = args[2]!;
|
|
2448
|
+
if (arg0.type !== 'identifier') return;
|
|
2449
|
+
if (arg1.type !== 'string') return;
|
|
2450
|
+
const key = arg1.text.replace(/^['"]|['"]$/g, '');
|
|
2451
|
+
if (!key) return;
|
|
2452
|
+
// Phase 8.3e: { value: fn } → obj.key pts to fn
|
|
2453
|
+
const target = findDescriptorValue(arg2);
|
|
2454
|
+
if (target) {
|
|
2455
|
+
setTypeMapEntry(typeMap, `${arg0.text}.${key}`, target, 0.85);
|
|
2456
|
+
}
|
|
2457
|
+
// Phase 8.3f: { get: getter } and/or { set: setter } → this inside each accessor is arg0 (obj)
|
|
2458
|
+
// Key format: '<accessorName>:this' — colon is a reserved separator used only by this phase.
|
|
2459
|
+
// JS identifiers cannot contain ':', so this key never collides with real variable names.
|
|
2460
|
+
for (const accessor of findDescriptorAccessors(arg2)) {
|
|
2461
|
+
setTypeMapEntry(typeMap, `${accessor}:this`, arg0.text, 0.85);
|
|
2462
|
+
}
|
|
2463
|
+
} else {
|
|
2464
|
+
// defineProperties
|
|
2465
|
+
if (args.length < 2) return;
|
|
2466
|
+
const arg0 = args[0]!,
|
|
2467
|
+
arg1 = args[1]!;
|
|
2468
|
+
if (arg0.type !== 'identifier') return;
|
|
2469
|
+
if (arg1.type !== 'object') return;
|
|
2470
|
+
for (let i = 0; i < arg1.childCount; i++) {
|
|
2471
|
+
const pair = arg1.child(i);
|
|
2472
|
+
if (pair?.type !== 'pair') continue;
|
|
2473
|
+
const keyN = pair.childForFieldName('key');
|
|
2474
|
+
const valN = pair.childForFieldName('value');
|
|
2475
|
+
if (!keyN || !valN) continue;
|
|
2476
|
+
const key = keyN.type === 'string' ? keyN.text.replace(/^['"]|['"]$/g, '') : keyN.text;
|
|
2477
|
+
const target = findDescriptorValue(valN);
|
|
2478
|
+
if (!target) continue;
|
|
2479
|
+
setTypeMapEntry(typeMap, `${arg0.text}.${key}`, target, 0.85);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
/** Return the identifier text of the `value` field in a property descriptor object. */
|
|
2485
|
+
function findDescriptorValue(desc: TreeSitterNode): string | undefined {
|
|
2486
|
+
if (desc.type !== 'object') return undefined;
|
|
2487
|
+
for (let i = 0; i < desc.childCount; i++) {
|
|
2488
|
+
const pair = desc.child(i);
|
|
2489
|
+
if (pair?.type !== 'pair') continue;
|
|
2490
|
+
const key = pair.childForFieldName('key');
|
|
2491
|
+
const val = pair.childForFieldName('value');
|
|
2492
|
+
if (key?.text === 'value' && val?.type === 'identifier') return val.text;
|
|
2493
|
+
}
|
|
2494
|
+
return undefined;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
/**
|
|
2498
|
+
* Phase 8.3f: return the identifier texts of all `get` and `set` accessors in a property
|
|
2499
|
+
* descriptor. `{ get: getter, set: setter }` → ['getter', 'setter'].
|
|
2500
|
+
* Returns all accessors so that each one gets a `callerName:this = obj` typeMap entry.
|
|
2501
|
+
*/
|
|
2502
|
+
function findDescriptorAccessors(desc: TreeSitterNode): string[] {
|
|
2503
|
+
if (desc.type !== 'object') return [];
|
|
2504
|
+
const result: string[] = [];
|
|
2505
|
+
for (let i = 0; i < desc.childCount; i++) {
|
|
2506
|
+
const pair = desc.child(i);
|
|
2507
|
+
if (pair?.type !== 'pair') continue;
|
|
2508
|
+
const key = pair.childForFieldName('key');
|
|
2509
|
+
const val = pair.childForFieldName('value');
|
|
2510
|
+
if ((key?.text === 'get' || key?.text === 'set') && val?.type === 'identifier') {
|
|
2511
|
+
result.push(val.text);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
return result;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
/** Seed composite pts keys for each property in a prototype object literal. */
|
|
2518
|
+
function seedProtoProperties(
|
|
2519
|
+
varName: string,
|
|
2520
|
+
proto: TreeSitterNode,
|
|
2521
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2522
|
+
): void {
|
|
2523
|
+
for (let i = 0; i < proto.childCount; i++) {
|
|
2524
|
+
const child = proto.child(i);
|
|
2525
|
+
if (!child) continue;
|
|
2526
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
2527
|
+
setTypeMapEntry(typeMap, `${varName}.${child.text}`, child.text, 0.85);
|
|
2528
|
+
} else if (child.type === 'pair') {
|
|
2529
|
+
const keyN = child.childForFieldName('key');
|
|
2530
|
+
const valN = child.childForFieldName('value');
|
|
2531
|
+
if (!keyN || !valN || valN.type !== 'identifier') continue;
|
|
2532
|
+
const key = keyN.type === 'string' ? keyN.text.replace(/^['"]|['"]$/g, '') : keyN.text;
|
|
2533
|
+
setTypeMapEntry(typeMap, `${varName}.${key}`, valN.text, 0.85);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
/**
|
|
2539
|
+
* Phase 8.3c: record argument-to-parameter bindings at call sites.
|
|
2540
|
+
*
|
|
2541
|
+
* For each `f(x, y)` where the callee is a simple identifier and an argument
|
|
2542
|
+
* is a simple identifier, emits a ParamBinding so the pts solver can add
|
|
2543
|
+
* constraint: pts(param_i_of_f) ⊇ pts(arg_i). The solver uses the
|
|
2544
|
+
* definitionParams map to resolve the actual parameter names.
|
|
2545
|
+
*
|
|
2546
|
+
* Scope: intra-module only (the solver only materialises constraints for
|
|
2547
|
+
* locally-defined callees, so cross-module calls produce no spurious flow).
|
|
2548
|
+
*/
|
|
2549
|
+
function collectParamBindings(node: TreeSitterNode, paramBindings: ParamBinding[]): void {
|
|
2550
|
+
const fn = node.childForFieldName('function');
|
|
2551
|
+
const args = node.childForFieldName('arguments') ?? findChild(node, 'arguments');
|
|
2552
|
+
if (fn?.type === 'identifier' && !BUILTIN_GLOBALS.has(fn.text) && args) {
|
|
2553
|
+
let argIdx = 0;
|
|
2554
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2555
|
+
const child = args.child(i);
|
|
2556
|
+
if (!child) continue;
|
|
2557
|
+
const ct = child.type;
|
|
2558
|
+
if (ct === ',' || ct === '(' || ct === ')') continue;
|
|
2559
|
+
if (ct === 'identifier' && !BUILTIN_GLOBALS.has(child.text)) {
|
|
2560
|
+
paramBindings.push({ callee: fn.text, argIndex: argIdx, argName: child.text });
|
|
2561
|
+
} else if (ct === 'spread_element') {
|
|
2562
|
+
// f(...[a, b]) — inline array literal: expand each element as a direct param binding.
|
|
2563
|
+
const inner =
|
|
2564
|
+
child.childForFieldName('argument') ?? (child.childCount > 1 ? child.child(1) : null);
|
|
2565
|
+
if (inner?.type === 'array') {
|
|
2566
|
+
let elemCount = 0;
|
|
2567
|
+
for (let j = 0; j < inner.childCount; j++) {
|
|
2568
|
+
const elem = inner.child(j);
|
|
2569
|
+
if (!elem) continue;
|
|
2570
|
+
if (elem.type === ',' || elem.type === '[' || elem.type === ']') continue;
|
|
2571
|
+
if (elem.type === 'identifier' && !BUILTIN_GLOBALS.has(elem.text)) {
|
|
2572
|
+
paramBindings.push({
|
|
2573
|
+
callee: fn.text,
|
|
2574
|
+
argIndex: argIdx + elemCount,
|
|
2575
|
+
argName: elem.text,
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
elemCount++;
|
|
2579
|
+
}
|
|
2580
|
+
// Advance by the exact number of slots this spread occupies and skip
|
|
2581
|
+
// the unconditional argIdx++ below so that zero-element spreads (...[])
|
|
2582
|
+
// do not shift subsequent argument indices.
|
|
2583
|
+
argIdx += elemCount;
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
argIdx++;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
/** Collection constructors whose argument is treated as an element source. */
|
|
2593
|
+
const COLLECTION_CTOR_SET = new Set(['Set', 'Map']);
|
|
2594
|
+
|
|
2595
|
+
/**
|
|
2596
|
+
* Phase 8.3e: Extract array-element bindings from `const arr = [fn1, fn2]` patterns.
|
|
2597
|
+
* Emits an ArrayElemBinding for each identifier element in an array literal assigned
|
|
2598
|
+
* to a variable.
|
|
2599
|
+
*/
|
|
2600
|
+
function collectArrayElemBindings(
|
|
2601
|
+
node: TreeSitterNode,
|
|
2602
|
+
arrayElemBindings: ArrayElemBinding[],
|
|
2603
|
+
): void {
|
|
2604
|
+
const nameN = node.childForFieldName('name');
|
|
2605
|
+
const valueN = node.childForFieldName('value');
|
|
2606
|
+
if (nameN?.type === 'identifier' && valueN?.type === 'array') {
|
|
2607
|
+
let idx = 0;
|
|
2608
|
+
for (let i = 0; i < valueN.childCount; i++) {
|
|
2609
|
+
const elem = valueN.child(i);
|
|
2610
|
+
if (!elem) continue;
|
|
2611
|
+
if (elem.type === ',' || elem.type === '[' || elem.type === ']') continue;
|
|
2612
|
+
if (elem.type === 'identifier' && !BUILTIN_GLOBALS.has(elem.text)) {
|
|
2613
|
+
arrayElemBindings.push({ arrayName: nameN.text, index: idx, elemName: elem.text });
|
|
2614
|
+
}
|
|
2615
|
+
idx++;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
/**
|
|
2621
|
+
* Phase 8.3e collectors (spread-argument, Array.from, collection-wrap, for-of
|
|
2622
|
+
* bindings), invoked from runContextCollectorWalk:
|
|
2623
|
+
*
|
|
2624
|
+
* - Spread: `f(...arr)` → SpreadArgBinding
|
|
2625
|
+
* - Array.from: `Array.from(src, cb)` → ArrayCallbackBinding
|
|
2626
|
+
* - Collection wrap: `new Set(arr)` / `new Map(arr)` → FnRefBinding lhs=s[*] rhs=arr[*]
|
|
2627
|
+
* - For-of: `for (const x of arr)` → ForOfBinding
|
|
2628
|
+
*/
|
|
2629
|
+
function collectSpreadAndArrayFromBindings(
|
|
2630
|
+
node: TreeSitterNode,
|
|
2631
|
+
spreadArgBindings: SpreadArgBinding[],
|
|
2632
|
+
arrayCallbackBindings: ArrayCallbackBinding[],
|
|
2633
|
+
): void {
|
|
2634
|
+
const fn = node.childForFieldName('function');
|
|
2635
|
+
const argsNode = node.childForFieldName('arguments') ?? findChild(node, 'arguments');
|
|
2636
|
+
|
|
2637
|
+
// Spread: f(...arr)
|
|
2638
|
+
if (fn?.type === 'identifier' && !BUILTIN_GLOBALS.has(fn.text) && argsNode) {
|
|
2639
|
+
let argIdx = 0;
|
|
2640
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
2641
|
+
const child = argsNode.child(i);
|
|
2642
|
+
if (!child) continue;
|
|
2643
|
+
if (child.type === ',' || child.type === '(' || child.type === ')') continue;
|
|
2644
|
+
if (child.type === 'spread_element') {
|
|
2645
|
+
const spreadTarget =
|
|
2646
|
+
child.childForFieldName('argument') ?? (child.childCount > 1 ? child.child(1) : null);
|
|
2647
|
+
if (spreadTarget?.type === 'identifier' && !BUILTIN_GLOBALS.has(spreadTarget.text)) {
|
|
2648
|
+
spreadArgBindings.push({
|
|
2649
|
+
callee: fn.text,
|
|
2650
|
+
arrayName: spreadTarget.text,
|
|
2651
|
+
startIndex: argIdx,
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
argIdx++;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// Array.from(source, cb)
|
|
2660
|
+
if (fn?.type === 'member_expression' && argsNode) {
|
|
2661
|
+
const obj = fn.childForFieldName('object');
|
|
2662
|
+
const prop = fn.childForFieldName('property');
|
|
2663
|
+
if (obj?.text === 'Array' && prop?.text === 'from') {
|
|
2664
|
+
const fnArgs: TreeSitterNode[] = [];
|
|
2665
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
2666
|
+
const child = argsNode.child(i);
|
|
2667
|
+
if (!child) continue;
|
|
2668
|
+
if (child.type === ',' || child.type === '(' || child.type === ')') continue;
|
|
2669
|
+
fnArgs.push(child);
|
|
2670
|
+
}
|
|
2671
|
+
if (fnArgs.length >= 2) {
|
|
2672
|
+
const srcArg = fnArgs[0]!;
|
|
2673
|
+
const cbArg = fnArgs[1]!;
|
|
2674
|
+
if (
|
|
2675
|
+
srcArg.type === 'identifier' &&
|
|
2676
|
+
!BUILTIN_GLOBALS.has(srcArg.text) &&
|
|
2677
|
+
cbArg.type === 'identifier' &&
|
|
2678
|
+
!BUILTIN_GLOBALS.has(cbArg.text)
|
|
2679
|
+
) {
|
|
2680
|
+
arrayCallbackBindings.push({ sourceName: srcArg.text, calleeName: cbArg.text });
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
/** Collection wrap: `const s = new Set(arr)` or `new Map(arr)` (variable_declarator). */
|
|
2688
|
+
function collectCollectionWrapBinding(node: TreeSitterNode, fnRefBindings: FnRefBinding[]): void {
|
|
2689
|
+
const nameN = node.childForFieldName('name');
|
|
2690
|
+
const valueN = node.childForFieldName('value');
|
|
2691
|
+
if (nameN?.type === 'identifier' && valueN?.type === 'new_expression') {
|
|
2692
|
+
const ctor = valueN.childForFieldName('constructor');
|
|
2693
|
+
const args = valueN.childForFieldName('arguments');
|
|
2694
|
+
if (ctor && COLLECTION_CTOR_SET.has(ctor.text) && args) {
|
|
2695
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2696
|
+
const arg = args.child(i);
|
|
2697
|
+
if (!arg || arg.type === '(' || arg.type === ')') continue;
|
|
2698
|
+
if (arg.type === 'identifier' && !BUILTIN_GLOBALS.has(arg.text)) {
|
|
2699
|
+
fnRefBindings.push({ lhs: `${nameN.text}[*]`, rhs: `${arg.text}[*]` });
|
|
2700
|
+
break;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
/** For-of: `for (const x of arr)` (for_in_statement with an `of` keyword). */
|
|
2708
|
+
function collectForOfBinding(
|
|
2709
|
+
node: TreeSitterNode,
|
|
2710
|
+
enclosingFunc: string,
|
|
2711
|
+
forOfBindings: ForOfBinding[],
|
|
2712
|
+
): void {
|
|
2713
|
+
let isForOf = false;
|
|
2714
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2715
|
+
if (node.child(i)?.text === 'of') {
|
|
2716
|
+
isForOf = true;
|
|
2717
|
+
break;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
if (!isForOf) return;
|
|
2721
|
+
const right = node.childForFieldName('right');
|
|
2722
|
+
if (right?.type !== 'identifier' || BUILTIN_GLOBALS.has(right.text)) return;
|
|
2723
|
+
const left = node.childForFieldName('left');
|
|
2724
|
+
let varName: string | null = null;
|
|
2725
|
+
if (left?.type === 'identifier') {
|
|
2726
|
+
varName = left.text;
|
|
2727
|
+
} else if (left) {
|
|
2728
|
+
for (let i = 0; i < left.childCount; i++) {
|
|
2729
|
+
const lc = left.child(i);
|
|
2730
|
+
if (lc?.type === 'variable_declarator') {
|
|
2731
|
+
const nc = lc.childForFieldName('name');
|
|
2732
|
+
if (nc?.type === 'identifier') {
|
|
2733
|
+
varName = nc.text;
|
|
2734
|
+
break;
|
|
2735
|
+
}
|
|
2736
|
+
} else if (
|
|
2737
|
+
lc?.type === 'identifier' &&
|
|
2738
|
+
lc.text !== 'const' &&
|
|
2739
|
+
lc.text !== 'let' &&
|
|
2740
|
+
lc.text !== 'var'
|
|
2741
|
+
) {
|
|
2742
|
+
varName = lc.text;
|
|
2743
|
+
break;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
if (varName && !BUILTIN_GLOBALS.has(varName)) {
|
|
2748
|
+
forOfBindings.push({ varName, sourceName: right.text, enclosingFunc });
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
/**
|
|
2753
|
+
* Phase 8.3f: record object-destructuring rest-parameter bindings from function definitions.
|
|
2754
|
+
*
|
|
2755
|
+
* For each `function f({ a, ...rest })` (or arrow/function-expression equivalent),
|
|
2756
|
+
* records { callee: 'f', restName: 'rest', argIndex: N }. Also covers class methods
|
|
2757
|
+
* (`callee: 'ClassName.method'`) and object-literal methods (`callee: 'method'`).
|
|
2758
|
+
* The edge builder uses these to seed typeMap[rest] = { type: argName } when f(obj)
|
|
2759
|
+
* is called with an identifier, enabling `rest.method()` calls to resolve.
|
|
2760
|
+
*/
|
|
2761
|
+
function collectObjectRestParams(
|
|
2762
|
+
node: TreeSitterNode,
|
|
2763
|
+
t: string,
|
|
2764
|
+
currentClass: string | null,
|
|
2765
|
+
bindings: ObjectRestParamBinding[],
|
|
2766
|
+
): void {
|
|
2767
|
+
let fnName: string | null = null;
|
|
2768
|
+
let paramsNode: TreeSitterNode | null = null;
|
|
2769
|
+
|
|
2770
|
+
if (t === 'function_declaration' || t === 'generator_function_declaration') {
|
|
2771
|
+
const nameN = node.childForFieldName('name');
|
|
2772
|
+
if (nameN?.type === 'identifier') fnName = nameN.text;
|
|
2773
|
+
paramsNode = node.childForFieldName('parameters') ?? findChild(node, 'formal_parameters');
|
|
2774
|
+
} else if (t === 'variable_declarator') {
|
|
2775
|
+
const nameN = node.childForFieldName('name');
|
|
2776
|
+
const valueN = node.childForFieldName('value');
|
|
2777
|
+
if (nameN?.type === 'identifier' && valueN) {
|
|
2778
|
+
const vt = valueN.type;
|
|
2779
|
+
if (vt === 'arrow_function' || vt === 'function_expression' || vt === 'generator_function') {
|
|
2780
|
+
fnName = nameN.text;
|
|
2781
|
+
paramsNode =
|
|
2782
|
+
valueN.childForFieldName('parameters') ?? findChild(valueN, 'formal_parameters');
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
} else if (t === 'method_definition') {
|
|
2786
|
+
// class method: `class Foo { bar({ a, ...rest }) {} }`
|
|
2787
|
+
// object-literal shorthand method: `{ bar({ a, ...rest }) {} }`
|
|
2788
|
+
const nameN = node.childForFieldName('name');
|
|
2789
|
+
if (nameN) {
|
|
2790
|
+
fnName = currentClass ? `${currentClass}.${nameN.text}` : nameN.text;
|
|
2791
|
+
paramsNode = node.childForFieldName('parameters') ?? findChild(node, 'formal_parameters');
|
|
2792
|
+
}
|
|
2793
|
+
} else if (t === 'pair') {
|
|
2794
|
+
// object-literal method: `{ bar: function({ a, ...rest }) {} }`
|
|
2795
|
+
// Skip computed property keys (e.g. `{ [Symbol.iterator]: function({ ...rest }) {} }`)
|
|
2796
|
+
// because `callee: '[Symbol.iterator]'` can never match a paramBinding callee.
|
|
2797
|
+
const keyN = node.childForFieldName('key');
|
|
2798
|
+
const valueN = node.childForFieldName('value');
|
|
2799
|
+
if (keyN && valueN && keyN.type !== 'computed_property_name') {
|
|
2800
|
+
const vt = valueN.type;
|
|
2801
|
+
if (vt === 'arrow_function' || vt === 'function_expression' || vt === 'generator_function') {
|
|
2802
|
+
fnName = keyN.type === 'string' ? keyN.text.slice(1, -1) : keyN.text;
|
|
2803
|
+
paramsNode =
|
|
2804
|
+
valueN.childForFieldName('parameters') ?? findChild(valueN, 'formal_parameters');
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
if (fnName && paramsNode) {
|
|
2810
|
+
let paramIdx = 0;
|
|
2811
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
2812
|
+
const child = paramsNode.child(i);
|
|
2813
|
+
if (!child) continue;
|
|
2814
|
+
const ct = child.type;
|
|
2815
|
+
if (ct === ',' || ct === '(' || ct === ')') continue;
|
|
2816
|
+
if (ct === 'object_pattern') {
|
|
2817
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
2818
|
+
const inner = child.child(j);
|
|
2819
|
+
if (!inner) continue;
|
|
2820
|
+
if (inner.type === 'rest_pattern' || inner.type === 'rest_element') {
|
|
2821
|
+
// rest_pattern node: `...identifier` — the identifier is at child index 1
|
|
2822
|
+
const restId = inner.child(1) ?? inner.childForFieldName('name');
|
|
2823
|
+
if (restId?.type === 'identifier') {
|
|
2824
|
+
bindings.push({ callee: fnName, restName: restId.text, argIndex: paramIdx });
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
paramIdx++;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
/**
|
|
2835
|
+
* Phase 8.3f: collect object-property bindings from object literals.
|
|
2836
|
+
*
|
|
2837
|
+
* `const obj = { e4 }` → `{ objectName: "obj", propName: "e4", valueName: "e4" }`
|
|
2838
|
+
* `const obj = { e1: fn }` → `{ objectName: "obj", propName: "e1", valueName: "fn" }`
|
|
2839
|
+
*
|
|
2840
|
+
* Only tracks shorthand and `key: identifier` pairs; skips function literals.
|
|
2841
|
+
*/
|
|
2842
|
+
function collectObjectPropBindings(node: TreeSitterNode, bindings: ObjectPropBinding[]): void {
|
|
2843
|
+
const nameN = node.childForFieldName('name');
|
|
2844
|
+
const valueN = node.childForFieldName('value');
|
|
2845
|
+
if (nameN?.type === 'identifier' && valueN?.type === 'object') {
|
|
2846
|
+
const objectName = nameN.text;
|
|
2847
|
+
for (let i = 0; i < valueN.childCount; i++) {
|
|
2848
|
+
const child = valueN.child(i);
|
|
2849
|
+
if (!child) continue;
|
|
2850
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
2851
|
+
bindings.push({ objectName, propName: child.text, valueName: child.text });
|
|
2852
|
+
} else if (child.type === 'pair') {
|
|
2853
|
+
const keyN = child.childForFieldName('key');
|
|
2854
|
+
const valN = child.childForFieldName('value');
|
|
2855
|
+
if (
|
|
2856
|
+
keyN?.type === 'property_identifier' &&
|
|
2857
|
+
valN?.type === 'identifier' &&
|
|
2858
|
+
!BUILTIN_GLOBALS.has(valN.text)
|
|
2859
|
+
) {
|
|
2860
|
+
bindings.push({ objectName, propName: keyN.text, valueName: valN.text });
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
function extractReceiverName(objNode: TreeSitterNode | null): string | undefined {
|
|
2868
|
+
if (!objNode) return undefined;
|
|
2869
|
+
const t = objNode.type;
|
|
2870
|
+
if (t === 'identifier' || t === 'this' || t === 'super') return objNode.text;
|
|
2871
|
+
// `(new Foo(...)).method()` — extract the constructor name so the resolver can
|
|
2872
|
+
// look up `Foo.method` directly without relying on a text-based regex heuristic.
|
|
2873
|
+
if (t === 'new_expression') {
|
|
2874
|
+
const name = extractNewExprTypeName(objNode);
|
|
2875
|
+
if (name) return name;
|
|
2876
|
+
}
|
|
2877
|
+
if (t === 'parenthesized_expression') {
|
|
2878
|
+
// Only one level of parentheses is unwrapped here. Doubly-nested parens
|
|
2879
|
+
// (e.g. `((new Dog())).bark()`) and cast expressions inside parens
|
|
2880
|
+
// (e.g. `(new Dog() as Animal).bark()`) fall through to raw-text handling
|
|
2881
|
+
// below and are caught by the regex fallback in call-resolver.ts.
|
|
2882
|
+
for (let i = 0; i < objNode.childCount; i++) {
|
|
2883
|
+
const child = objNode.child(i);
|
|
2884
|
+
if (child?.type === 'new_expression') {
|
|
2885
|
+
const name = extractNewExprTypeName(child);
|
|
2886
|
+
if (name) return name;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
return objNode.text;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2894
|
+
const fnType = fn.type;
|
|
2895
|
+
if (fnType === 'identifier') {
|
|
2896
|
+
if (fn.text === 'eval') {
|
|
2897
|
+
// eval(code) — dynamic code execution; capture first arg if it's a string literal
|
|
2898
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2899
|
+
let keyExpr: string | undefined;
|
|
2900
|
+
if (args) {
|
|
2901
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2902
|
+
const child = args.child(i);
|
|
2903
|
+
if (!child) continue;
|
|
2904
|
+
const t = child.type;
|
|
2905
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
2906
|
+
if (t === 'string' || t === 'template_string') keyExpr = child.text;
|
|
2907
|
+
break;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
return {
|
|
2911
|
+
name: '<dynamic:eval>',
|
|
2912
|
+
line: nodeStartLine(callNode),
|
|
2913
|
+
dynamic: true,
|
|
2914
|
+
dynamicKind: 'eval',
|
|
2915
|
+
keyExpr,
|
|
2916
|
+
};
|
|
2917
|
+
}
|
|
2918
|
+
return { name: fn.text, line: nodeStartLine(callNode) };
|
|
2919
|
+
}
|
|
2920
|
+
if (fnType === 'member_expression') {
|
|
2921
|
+
return extractMemberExprCallInfo(fn, callNode);
|
|
2922
|
+
}
|
|
2923
|
+
if (fnType === 'subscript_expression') {
|
|
2924
|
+
return extractSubscriptCallInfo(fn, callNode);
|
|
2925
|
+
}
|
|
2926
|
+
return null;
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
/** Return the first non-punctuation argument node from a call_expression. */
|
|
2930
|
+
function getFirstCallArg(callNode: TreeSitterNode): TreeSitterNode | null {
|
|
2931
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2932
|
+
if (!args) return null;
|
|
2933
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2934
|
+
const child = args.child(i);
|
|
2935
|
+
if (!child) continue;
|
|
2936
|
+
const t = child.type;
|
|
2937
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
2938
|
+
return child;
|
|
2939
|
+
}
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
/** Extract the logical callee from a Reflect.apply/call/construct first-arg. */
|
|
2944
|
+
function extractReflectCalleeFromArg(firstArg: TreeSitterNode | null, callLine: number): Call {
|
|
2945
|
+
if (firstArg?.type === 'identifier') {
|
|
2946
|
+
return { name: firstArg.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
2947
|
+
}
|
|
2948
|
+
if (firstArg?.type === 'member_expression') {
|
|
2949
|
+
const innerProp = firstArg.childForFieldName('property');
|
|
2950
|
+
if (innerProp?.type === 'identifier') {
|
|
2951
|
+
return {
|
|
2952
|
+
name: innerProp.text,
|
|
2953
|
+
line: callLine,
|
|
2954
|
+
dynamic: true,
|
|
2955
|
+
dynamicKind: 'reflection',
|
|
2956
|
+
receiver: extractReceiverName(firstArg.childForFieldName('object')),
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
return {
|
|
2961
|
+
name: '<dynamic:unresolved>',
|
|
2962
|
+
line: callLine,
|
|
2963
|
+
dynamic: true,
|
|
2964
|
+
dynamicKind: 'unresolved-dynamic',
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
/** Extract call info from a member_expression function node (obj.method()). */
|
|
2969
|
+
function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2970
|
+
const obj = fn.childForFieldName('object');
|
|
2971
|
+
const prop = fn.childForFieldName('property');
|
|
2972
|
+
if (!prop) return null;
|
|
2973
|
+
|
|
2974
|
+
const callLine = nodeStartLine(callNode);
|
|
2975
|
+
const propText = prop.text;
|
|
2976
|
+
const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
|
|
2977
|
+
|
|
2978
|
+
// Reflect.apply(fn, thisArg, args) — extract the first arg as callee
|
|
2979
|
+
// Note: Reflect.call does not exist in the ECMAScript spec (only Reflect.apply, construct, get, etc.)
|
|
2980
|
+
if (isReflect && propText === 'apply') {
|
|
2981
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
// Reflect.construct(Target, args) — extract the constructor as the callee
|
|
2985
|
+
if (isReflect && propText === 'construct') {
|
|
2986
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// Reflect.get(target, prop) — property access via reflection
|
|
2990
|
+
if (isReflect && propText === 'get') {
|
|
2991
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2992
|
+
if (args) {
|
|
2993
|
+
let argIdx = 0;
|
|
2994
|
+
let firstArg: TreeSitterNode | null = null;
|
|
2995
|
+
let secondArg: TreeSitterNode | null = null;
|
|
2996
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2997
|
+
const child = args.child(i);
|
|
2998
|
+
if (!child) continue;
|
|
2999
|
+
const t = child.type;
|
|
3000
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
3001
|
+
if (argIdx === 0) firstArg = child;
|
|
3002
|
+
else if (argIdx === 1) secondArg = child;
|
|
3003
|
+
argIdx++;
|
|
3004
|
+
}
|
|
3005
|
+
if (secondArg) {
|
|
3006
|
+
const receiver = firstArg ? extractReceiverName(firstArg) : undefined;
|
|
3007
|
+
const st = secondArg.type;
|
|
3008
|
+
if (st === 'string' || st === 'string_fragment') {
|
|
3009
|
+
const propName = secondArg.text.replace(/['"]/g, '');
|
|
3010
|
+
if (propName) {
|
|
3011
|
+
return {
|
|
3012
|
+
name: propName,
|
|
3013
|
+
line: callLine,
|
|
3014
|
+
dynamic: true,
|
|
3015
|
+
dynamicKind: 'computed-literal',
|
|
3016
|
+
keyExpr: secondArg.text,
|
|
3017
|
+
receiver,
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
if (st === 'identifier') {
|
|
3022
|
+
return {
|
|
3023
|
+
name: '<dynamic:computed-key>',
|
|
3024
|
+
line: callLine,
|
|
3025
|
+
dynamic: true,
|
|
3026
|
+
dynamicKind: 'computed-key',
|
|
3027
|
+
keyExpr: secondArg.text,
|
|
3028
|
+
receiver,
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
return {
|
|
3034
|
+
name: '<dynamic:unresolved>',
|
|
3035
|
+
line: callLine,
|
|
3036
|
+
dynamic: true,
|
|
3037
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
// .call()/.apply()/.bind() — this-rebinding; target is statically known
|
|
3042
|
+
if (propText === 'call' || propText === 'apply' || propText === 'bind') {
|
|
3043
|
+
if (obj && obj.type === 'identifier')
|
|
3044
|
+
return { name: obj.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
3045
|
+
if (obj && obj.type === 'member_expression') {
|
|
3046
|
+
const innerProp = obj.childForFieldName('property');
|
|
3047
|
+
if (innerProp)
|
|
3048
|
+
return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
// Computed string property: obj["method"]() — target is a literal; resolvable
|
|
3053
|
+
const propType = prop.type;
|
|
3054
|
+
if (propType === 'string' || propType === 'string_fragment') {
|
|
3055
|
+
const methodName = propText.replace(/['"]/g, '');
|
|
3056
|
+
if (methodName) {
|
|
3057
|
+
const receiver = extractReceiverName(obj);
|
|
3058
|
+
return {
|
|
3059
|
+
name: methodName,
|
|
3060
|
+
line: callLine,
|
|
3061
|
+
dynamic: true,
|
|
3062
|
+
dynamicKind: 'computed-literal',
|
|
3063
|
+
receiver,
|
|
3064
|
+
};
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
const receiver = extractReceiverName(obj);
|
|
3069
|
+
return { name: propText, line: callLine, receiver };
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
/** Extract call info from a subscript_expression function node (obj[key]()). */
|
|
3073
|
+
function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
3074
|
+
const obj = fn.childForFieldName('object');
|
|
3075
|
+
const index = fn.childForFieldName('index');
|
|
3076
|
+
if (!index) return null;
|
|
3077
|
+
|
|
3078
|
+
const indexType = index.type;
|
|
3079
|
+
if (indexType === 'string' || indexType === 'template_string') {
|
|
3080
|
+
const methodName = index.text.replace(/['"`]/g, '');
|
|
3081
|
+
if (methodName && !methodName.includes('$')) {
|
|
3082
|
+
const receiver = extractReceiverName(obj);
|
|
3083
|
+
return {
|
|
3084
|
+
name: methodName,
|
|
3085
|
+
line: nodeStartLine(callNode),
|
|
3086
|
+
dynamic: true,
|
|
3087
|
+
dynamicKind: 'computed-literal',
|
|
3088
|
+
receiver,
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
// obj[variable]() — key is a variable; may be resolvable via pts (RES-1), else flagged
|
|
3094
|
+
if (indexType === 'identifier') {
|
|
3095
|
+
const receiver = extractReceiverName(obj);
|
|
3096
|
+
return {
|
|
3097
|
+
name: '<dynamic:computed-key>',
|
|
3098
|
+
line: nodeStartLine(callNode),
|
|
3099
|
+
dynamic: true,
|
|
3100
|
+
dynamicKind: 'computed-key',
|
|
3101
|
+
keyExpr: index.text,
|
|
3102
|
+
receiver,
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
// Any other index expression (binary, call, template with ${}…) — not statically resolvable
|
|
3107
|
+
return {
|
|
3108
|
+
name: '<dynamic:unresolved>',
|
|
3109
|
+
line: nodeStartLine(callNode),
|
|
3110
|
+
dynamic: true,
|
|
3111
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
/**
|
|
3116
|
+
* Callee names that idiomatically accept callback references. Used to gate
|
|
3117
|
+
* member_expression args in {@link extractCallbackReferenceCalls}: arguments
|
|
3118
|
+
* like `user.id` are only emitted as dynamic callback calls when the callee
|
|
3119
|
+
* is a known callback-accepting API (router/middleware, promises, array
|
|
3120
|
+
* methods, event emitters, scheduling APIs). This avoids false positives
|
|
3121
|
+
* from plain property reads passed as data, e.g. `store.set(user.id, user)`.
|
|
3122
|
+
*
|
|
3123
|
+
* Identifier args (e.g. `router.use(handleToken)`) are always emitted — the
|
|
3124
|
+
* collateral damage of dropping them is larger than the FP risk, since plain
|
|
3125
|
+
* identifier data args rarely collide with real function names.
|
|
3126
|
+
*/
|
|
3127
|
+
const CALLBACK_ACCEPTING_CALLEES: ReadonlySet<string> = new Set([
|
|
3128
|
+
// Express / router / middleware
|
|
3129
|
+
'use',
|
|
3130
|
+
'get',
|
|
3131
|
+
'post',
|
|
3132
|
+
'put',
|
|
3133
|
+
'delete',
|
|
3134
|
+
'patch',
|
|
3135
|
+
'options',
|
|
3136
|
+
'head',
|
|
3137
|
+
'all',
|
|
3138
|
+
// Promises
|
|
3139
|
+
'then',
|
|
3140
|
+
'catch',
|
|
3141
|
+
'finally',
|
|
3142
|
+
// Array iteration / reduction
|
|
3143
|
+
'map',
|
|
3144
|
+
'filter',
|
|
3145
|
+
'forEach',
|
|
3146
|
+
'find',
|
|
3147
|
+
'findIndex',
|
|
3148
|
+
'findLast',
|
|
3149
|
+
'findLastIndex',
|
|
3150
|
+
'some',
|
|
3151
|
+
'every',
|
|
3152
|
+
'reduce',
|
|
3153
|
+
'reduceRight',
|
|
3154
|
+
'flatMap',
|
|
3155
|
+
'sort',
|
|
3156
|
+
// Event emitters / DOM
|
|
3157
|
+
'on',
|
|
3158
|
+
'once',
|
|
3159
|
+
'off',
|
|
3160
|
+
'addListener',
|
|
3161
|
+
'removeListener',
|
|
3162
|
+
'addEventListener',
|
|
3163
|
+
'removeEventListener',
|
|
3164
|
+
'subscribe',
|
|
3165
|
+
'unsubscribe',
|
|
3166
|
+
// Scheduling / plain function callbacks
|
|
3167
|
+
'setTimeout',
|
|
3168
|
+
'setInterval',
|
|
3169
|
+
'setImmediate',
|
|
3170
|
+
'queueMicrotask',
|
|
3171
|
+
'requestAnimationFrame',
|
|
3172
|
+
'requestIdleCallback',
|
|
3173
|
+
'nextTick',
|
|
3174
|
+
// Commander / yargs / hooks
|
|
3175
|
+
'action',
|
|
3176
|
+
'command',
|
|
3177
|
+
]);
|
|
3178
|
+
|
|
3179
|
+
/**
|
|
3180
|
+
* HTTP-verb callees that double as Map/cache/repository method names (`get`,
|
|
3181
|
+
* `post`, `put`, `delete`, `patch`, `options`, `head`, `all`). Express/router
|
|
3182
|
+
* invocations always take a string-literal route path as the first argument
|
|
3183
|
+
* (`app.get('/path', handler)`), whereas Map-like APIs pass values/keys
|
|
3184
|
+
* (`cache.get(user.id)`). Requiring a string-literal first arg keeps real
|
|
3185
|
+
* route handlers covered while dropping the Map/cache false-positive surface.
|
|
3186
|
+
*
|
|
3187
|
+
* `use` and `all` without a path are legitimate middleware registrations, so
|
|
3188
|
+
* `use` is intentionally excluded here — it stays in the general allowlist.
|
|
3189
|
+
*/
|
|
3190
|
+
const HTTP_VERB_CALLEES: ReadonlySet<string> = new Set([
|
|
3191
|
+
'get',
|
|
3192
|
+
'post',
|
|
3193
|
+
'put',
|
|
3194
|
+
'delete',
|
|
3195
|
+
'patch',
|
|
3196
|
+
'options',
|
|
3197
|
+
'head',
|
|
3198
|
+
'all',
|
|
3199
|
+
]);
|
|
3200
|
+
|
|
3201
|
+
/**
|
|
3202
|
+
* Extract the callee's final name (function identifier or member expression
|
|
3203
|
+
* property) for callback-eligibility filtering. Returns null if the callee
|
|
3204
|
+
* shape is not analyzable (e.g. computed subscripts, IIFEs).
|
|
3205
|
+
*
|
|
3206
|
+
* Optional-chaining (`obj?.method(...)`) is handled transparently: in both
|
|
3207
|
+
* tree-sitter-javascript and tree-sitter-typescript grammars `obj?.method` is
|
|
3208
|
+
* still a `member_expression` (the `?.` appears as an `optional_chain` child),
|
|
3209
|
+
* so the property extraction below returns `method` as expected.
|
|
3210
|
+
*/
|
|
3211
|
+
function extractCalleeName(callNode: TreeSitterNode): string | null {
|
|
3212
|
+
const fn = callNode.childForFieldName('function');
|
|
3213
|
+
if (!fn) return null;
|
|
3214
|
+
if (fn.type === 'identifier') return fn.text;
|
|
3215
|
+
if (fn.type === 'member_expression') {
|
|
3216
|
+
const prop = fn.childForFieldName('property');
|
|
3217
|
+
return prop ? prop.text : null;
|
|
3218
|
+
}
|
|
3219
|
+
return null;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
/**
|
|
3223
|
+
* True iff the first argument of an arguments node is a string literal.
|
|
3224
|
+
* Used to distinguish Express/router route handlers (`app.get('/path', h)`)
|
|
3225
|
+
* from Map/cache APIs that reuse the same verb names (`cache.get(user.id)`).
|
|
3226
|
+
*/
|
|
3227
|
+
function firstArgIsStringLiteral(argsNode: TreeSitterNode): boolean {
|
|
3228
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
3229
|
+
const child = argsNode.child(i);
|
|
3230
|
+
if (!child) continue;
|
|
3231
|
+
// Skip parens and commas; the first non-punctuation child is the first arg.
|
|
3232
|
+
if (child.type === '(' || child.type === ',' || child.type === ')') continue;
|
|
3233
|
+
return child.type === 'string' || child.type === 'template_string';
|
|
3234
|
+
}
|
|
3235
|
+
return false;
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
/**
|
|
3239
|
+
* Extract Call entries for named function references passed as arguments.
|
|
3240
|
+
* e.g. `router.use(handleToken, checkAuth)` yields calls to handleToken and checkAuth.
|
|
3241
|
+
* `app.use(auth.validate)` yields a call to validate with receiver auth.
|
|
3242
|
+
* Skips literals, objects, arrays, anonymous functions, and call expressions (already handled).
|
|
3243
|
+
*
|
|
3244
|
+
* To avoid false positives where plain property reads are passed as data
|
|
3245
|
+
* (e.g. `store.set(user.id, user)` — `user.id` is a value, not a callback),
|
|
3246
|
+
* member_expression args are only emitted when the callee is in
|
|
3247
|
+
* {@link CALLBACK_ACCEPTING_CALLEES}. Identifier args are always emitted.
|
|
3248
|
+
*
|
|
3249
|
+
* HTTP-verb callees (`get`, `post`, `put`, `delete`, `patch`, `options`,
|
|
3250
|
+
* `head`, `all`) double as Map/cache/repository method names, so their
|
|
3251
|
+
* member-expr args are only emitted when the first argument is a string
|
|
3252
|
+
* literal route path — matching Express/router shape and skipping
|
|
3253
|
+
* `cache.get(user.id)`-style calls.
|
|
3254
|
+
*
|
|
3255
|
+
* `.call()` / `.apply()` / `.bind()` — the first arg is the `this` context (not a callback of
|
|
3256
|
+
* the enclosing function) and subsequent args flow into the delegated function's parameters.
|
|
3257
|
+
* Emitting them here would produce false-positive edges from the *calling* function.
|
|
3258
|
+
* This-rebinding (fn::this → ctx) is handled separately by extractThisCallBindingsWalk.
|
|
3259
|
+
*/
|
|
3260
|
+
function extractCallbackReferenceCalls(callNode: TreeSitterNode): Call[] {
|
|
3261
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
3262
|
+
if (!args) return [];
|
|
3263
|
+
|
|
3264
|
+
const calleeName = extractCalleeName(callNode);
|
|
3265
|
+
// .call() / .apply() / .bind() — the first arg is the `this` context (not a callback of
|
|
3266
|
+
// the enclosing function) and subsequent args flow into the delegated function's parameters.
|
|
3267
|
+
// Emitting them here would produce false-positive edges from the *calling* function.
|
|
3268
|
+
// This-rebinding (fn::this → ctx) is handled separately by extractThisCallBindingsWalk.
|
|
3269
|
+
if (calleeName === 'call' || calleeName === 'apply' || calleeName === 'bind') return [];
|
|
3270
|
+
|
|
3271
|
+
let memberExprArgsAllowed = calleeName !== null && CALLBACK_ACCEPTING_CALLEES.has(calleeName);
|
|
3272
|
+
if (memberExprArgsAllowed && calleeName !== null && HTTP_VERB_CALLEES.has(calleeName)) {
|
|
3273
|
+
// HTTP verbs require a string-literal route path to be treated as a
|
|
3274
|
+
// callback-accepting API; otherwise `cache.get(user.id)` etc. would
|
|
3275
|
+
// still emit `id` as a dynamic call.
|
|
3276
|
+
memberExprArgsAllowed = firstArgIsStringLiteral(args);
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
const result: Call[] = [];
|
|
3280
|
+
const callLine = nodeStartLine(callNode);
|
|
3281
|
+
|
|
3282
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
3283
|
+
const child = args.child(i);
|
|
3284
|
+
if (!child) continue;
|
|
3285
|
+
|
|
3286
|
+
if (child.type === 'identifier') {
|
|
3287
|
+
result.push({ name: child.text, line: callLine, dynamic: true });
|
|
3288
|
+
} else if (child.type === 'member_expression' && memberExprArgsAllowed) {
|
|
3289
|
+
const prop = child.childForFieldName('property');
|
|
3290
|
+
const obj = child.childForFieldName('object');
|
|
3291
|
+
if (prop) {
|
|
3292
|
+
const receiver = extractReceiverName(obj);
|
|
3293
|
+
result.push({ name: prop.text, line: callLine, dynamic: true, receiver });
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
return result;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
/**
|
|
3302
|
+
* Collect, from a call_expression node:
|
|
3303
|
+
* - `this(args)` call expressions → `{name: 'this', ...}` entries in `calls`
|
|
3304
|
+
* (where `this` is used as a function, not as a receiver)
|
|
3305
|
+
* - `fn.call(namedCtx, ...)` / `fn.apply(namedCtx, ...)` bindings →
|
|
3306
|
+
* `{ callee: 'fn', thisArg: 'namedCtx' }` entries in `thisCallBindings`
|
|
3307
|
+
*/
|
|
3308
|
+
function collectThisCallAndBindings(
|
|
3309
|
+
node: TreeSitterNode,
|
|
3310
|
+
calls: Call[],
|
|
3311
|
+
thisCallBindings: ThisCallBinding[],
|
|
3312
|
+
): void {
|
|
3313
|
+
const fn = node.childForFieldName('function');
|
|
3314
|
+
if (fn?.type === 'this') {
|
|
3315
|
+
calls.push({ name: 'this', line: nodeStartLine(node) });
|
|
3316
|
+
} else if (fn?.type === 'member_expression') {
|
|
3317
|
+
const obj = fn.childForFieldName('object');
|
|
3318
|
+
const prop = fn.childForFieldName('property');
|
|
3319
|
+
if (
|
|
3320
|
+
obj?.type === 'identifier' &&
|
|
3321
|
+
prop &&
|
|
3322
|
+
(prop.text === 'call' || prop.text === 'apply') &&
|
|
3323
|
+
!BUILTIN_GLOBALS.has(obj.text)
|
|
3324
|
+
) {
|
|
3325
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
3326
|
+
if (args) {
|
|
3327
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
3328
|
+
const child = args.child(i);
|
|
3329
|
+
if (!child) continue;
|
|
3330
|
+
const t = child.type;
|
|
3331
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
3332
|
+
// First real argument: only bind if it's a plain identifier
|
|
3333
|
+
if (
|
|
3334
|
+
t === 'identifier' &&
|
|
3335
|
+
!BUILTIN_GLOBALS.has(child.text) &&
|
|
3336
|
+
child.text !== 'undefined' &&
|
|
3337
|
+
child.text !== 'null'
|
|
3338
|
+
) {
|
|
3339
|
+
thisCallBindings.push({ callee: obj.text, thisArg: child.text });
|
|
3340
|
+
}
|
|
3341
|
+
break;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
/**
|
|
3349
|
+
* Outputs for {@link runCollectorWalk}. Required targets are collected on both
|
|
3350
|
+
* extraction paths; optional targets are path-specific:
|
|
3351
|
+
* - `imports` / `calls`+`thisCallBindings` / `classMemberDefs` — query path only
|
|
3352
|
+
* (the walk path's walkJavaScriptNode covers those node types itself).
|
|
3353
|
+
* - `funcPropDefs` — walk path only (the query path captures `fn.method = …`
|
|
3354
|
+
* assignments via the `assign_left`/`assign_right` query pattern).
|
|
3355
|
+
*/
|
|
3356
|
+
interface CollectorWalkTargets {
|
|
3357
|
+
definitions: Definition[];
|
|
3358
|
+
typeMap: Map<string, TypeMapEntry>;
|
|
3359
|
+
paramBindings: ParamBinding[];
|
|
3360
|
+
arrayElemBindings: ArrayElemBinding[];
|
|
3361
|
+
objectPropBindings: ObjectPropBinding[];
|
|
3362
|
+
newExpressions: string[];
|
|
3363
|
+
definePropertyReceivers: Map<string, string>;
|
|
3364
|
+
imports?: Import[];
|
|
3365
|
+
calls?: Call[];
|
|
3366
|
+
thisCallBindings?: ThisCallBinding[];
|
|
3367
|
+
classMemberDefs?: Definition[];
|
|
3368
|
+
funcPropDefs?: Definition[];
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
/**
|
|
3372
|
+
* Single-pass collector walk: one DFS that dispatches each node to every
|
|
3373
|
+
* collector interested in its type.
|
|
3374
|
+
*
|
|
3375
|
+
* This replaces what had grown to ten independent full-tree traversals (one
|
|
3376
|
+
* per collector). On WASM trees every node access (`child(i)`, `.type`,
|
|
3377
|
+
* `childForFieldName`) marshals through the JS↔WASM boundary, so traversal
|
|
3378
|
+
* count — not collector work — dominated extraction cost: the accumulated
|
|
3379
|
+
* per-collector walks made extraction ~2.4× slower between v3.11.2 and
|
|
3380
|
+
* v3.12.0 (7.5 → 17.7 ms/file on codegraph's own corpus).
|
|
3381
|
+
*
|
|
3382
|
+
* Collectors with bespoke traversal semantics stay separate:
|
|
3383
|
+
* - extractConstantsWalk / extractDestructuredBindingsWalk prune function
|
|
3384
|
+
* scopes and unwrap export statements on the way down;
|
|
3385
|
+
* - extractReturnTypeMapWalk / extractTypeMapWalk / extractSpreadForOfWalk /
|
|
3386
|
+
* extractObjectRestParamBindingsWalk thread enclosing-class context with
|
|
3387
|
+
* per-walk reset rules that intentionally differ (see each walk's comments).
|
|
3388
|
+
*/
|
|
3389
|
+
function runCollectorWalk(rootNode: TreeSitterNode, targets: CollectorWalkTargets): void {
|
|
3390
|
+
const walk = (node: TreeSitterNode, depth: number, inDynamicImport: boolean): void => {
|
|
3391
|
+
if (depth >= MAX_WALK_DEPTH) return;
|
|
3392
|
+
let childInDynamicImport = inDynamicImport;
|
|
3393
|
+
switch (node.type) {
|
|
3394
|
+
case 'call_expression': {
|
|
3395
|
+
// Matched import() calls suppress *dynamic-import* collection in their
|
|
3396
|
+
// argument subtree (mirrors the old walk's early return) while leaving
|
|
3397
|
+
// the subtree visible to every other collector. The !inDynamicImport
|
|
3398
|
+
// check runs first so nested import() calls are neither collected nor
|
|
3399
|
+
// re-matched.
|
|
3400
|
+
if (targets.imports && !inDynamicImport && collectDynamicImport(node, targets.imports)) {
|
|
3401
|
+
childInDynamicImport = true;
|
|
3402
|
+
}
|
|
3403
|
+
if (targets.calls && targets.thisCallBindings) {
|
|
3404
|
+
collectThisCallAndBindings(node, targets.calls, targets.thisCallBindings);
|
|
3405
|
+
}
|
|
3406
|
+
collectParamBindings(node, targets.paramBindings);
|
|
3407
|
+
collectDefinePropertyReceiver(node, targets.definePropertyReceivers);
|
|
3408
|
+
break;
|
|
3409
|
+
}
|
|
3410
|
+
case 'variable_declarator':
|
|
3411
|
+
collectArrayElemBindings(node, targets.arrayElemBindings);
|
|
3412
|
+
collectObjectPropBindings(node, targets.objectPropBindings);
|
|
3413
|
+
break;
|
|
3414
|
+
case 'expression_statement': {
|
|
3415
|
+
const expr = node.child(0);
|
|
3416
|
+
if (expr?.type === 'assignment_expression') {
|
|
3417
|
+
const lhs = expr.childForFieldName('left');
|
|
3418
|
+
const rhs = expr.childForFieldName('right');
|
|
3419
|
+
if (lhs && rhs) {
|
|
3420
|
+
handlePrototypeAssignment(lhs, rhs, targets.definitions, targets.typeMap);
|
|
3421
|
+
if (targets.funcPropDefs) handleFuncPropAssignment(lhs, rhs, targets.funcPropDefs);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
break;
|
|
3425
|
+
}
|
|
3426
|
+
case 'new_expression': {
|
|
3427
|
+
const name = extractNewExprTypeName(node);
|
|
3428
|
+
if (name) targets.newExpressions.push(name);
|
|
3429
|
+
break;
|
|
3430
|
+
}
|
|
3431
|
+
case 'decorator': {
|
|
3432
|
+
if (targets.calls) handleDecorator(node, targets.calls);
|
|
3433
|
+
break;
|
|
3434
|
+
}
|
|
3435
|
+
case 'field_definition':
|
|
3436
|
+
case 'public_field_definition':
|
|
3437
|
+
if (targets.classMemberDefs) handleFieldDef(node, targets.classMemberDefs);
|
|
3438
|
+
break;
|
|
3439
|
+
case 'class_static_block':
|
|
3440
|
+
if (targets.classMemberDefs) handleStaticBlock(node, targets.classMemberDefs);
|
|
3441
|
+
break;
|
|
3442
|
+
}
|
|
3443
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3444
|
+
walk(node.child(i)!, depth + 1, childInDynamicImport);
|
|
3445
|
+
}
|
|
3446
|
+
};
|
|
3447
|
+
walk(rootNode, 0, false);
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
function findAnonymousCallback(argsNode: TreeSitterNode): TreeSitterNode | null {
|
|
3451
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
3452
|
+
const child = argsNode.child(i);
|
|
3453
|
+
if (child && (child.type === 'arrow_function' || child.type === 'function_expression')) {
|
|
3454
|
+
return child;
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
return null;
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3460
|
+
function findFirstStringArg(argsNode: TreeSitterNode): string | null {
|
|
3461
|
+
for (let i = 0; i < argsNode.childCount; i++) {
|
|
3462
|
+
const child = argsNode.child(i);
|
|
3463
|
+
if (child && child.type === 'string') {
|
|
3464
|
+
return child.text.replace(/['"]/g, '');
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
return null;
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
function walkCallChain(startNode: TreeSitterNode, methodName: string): TreeSitterNode | null {
|
|
3471
|
+
let current: TreeSitterNode | null = startNode;
|
|
3472
|
+
while (current) {
|
|
3473
|
+
const curType = current.type;
|
|
3474
|
+
if (curType === 'call_expression') {
|
|
3475
|
+
const fn = current.childForFieldName('function');
|
|
3476
|
+
if (fn && fn.type === 'member_expression') {
|
|
3477
|
+
const prop = fn.childForFieldName('property');
|
|
3478
|
+
if (prop && prop.text === methodName) {
|
|
3479
|
+
return current;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
current = fn;
|
|
3483
|
+
} else if (curType === 'member_expression') {
|
|
3484
|
+
current = current.childForFieldName('object');
|
|
3485
|
+
} else {
|
|
3486
|
+
break;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return null;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
const EXPRESS_METHODS: Set<string> = new Set([
|
|
3493
|
+
'get',
|
|
3494
|
+
'post',
|
|
3495
|
+
'put',
|
|
3496
|
+
'delete',
|
|
3497
|
+
'patch',
|
|
3498
|
+
'options',
|
|
3499
|
+
'head',
|
|
3500
|
+
'all',
|
|
3501
|
+
'use',
|
|
3502
|
+
]);
|
|
3503
|
+
const EVENT_METHODS: Set<string> = new Set(['on', 'once', 'addEventListener', 'addListener']);
|
|
3504
|
+
|
|
3505
|
+
function extractCallbackDefinition(
|
|
3506
|
+
callNode: TreeSitterNode,
|
|
3507
|
+
fn?: TreeSitterNode | null,
|
|
3508
|
+
): Definition | null {
|
|
3509
|
+
if (!fn) fn = callNode.childForFieldName('function');
|
|
3510
|
+
if (fn?.type !== 'member_expression') return null;
|
|
3511
|
+
|
|
3512
|
+
const prop = fn.childForFieldName('property');
|
|
3513
|
+
if (!prop) return null;
|
|
3514
|
+
const method = prop.text;
|
|
3515
|
+
|
|
3516
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
3517
|
+
if (!args) return null;
|
|
3518
|
+
|
|
3519
|
+
// Commander: .action(callback) with .command('name') in chain
|
|
3520
|
+
if (method === 'action') {
|
|
3521
|
+
const cb = findAnonymousCallback(args);
|
|
3522
|
+
if (!cb) return null;
|
|
3523
|
+
const commandCall = walkCallChain(fn.childForFieldName('object')!, 'command');
|
|
3524
|
+
if (!commandCall) return null;
|
|
3525
|
+
const cmdArgs =
|
|
3526
|
+
commandCall.childForFieldName('arguments') || findChild(commandCall, 'arguments');
|
|
3527
|
+
if (!cmdArgs) return null;
|
|
3528
|
+
const cmdName = findFirstStringArg(cmdArgs);
|
|
3529
|
+
if (!cmdName) return null;
|
|
3530
|
+
const firstWord = cmdName.split(/\s/)[0]!;
|
|
3531
|
+
return {
|
|
3532
|
+
name: `command:${firstWord}`,
|
|
3533
|
+
kind: 'function',
|
|
3534
|
+
line: nodeStartLine(cb),
|
|
3535
|
+
endLine: nodeEndLine(cb),
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
// Express: app.get('/path', callback)
|
|
3540
|
+
if (EXPRESS_METHODS.has(method)) {
|
|
3541
|
+
const strArg = findFirstStringArg(args);
|
|
3542
|
+
if (!strArg?.startsWith('/')) return null;
|
|
3543
|
+
const cb = findAnonymousCallback(args);
|
|
3544
|
+
if (!cb) return null;
|
|
3545
|
+
return {
|
|
3546
|
+
name: `route:${method.toUpperCase()} ${strArg}`,
|
|
3547
|
+
kind: 'function',
|
|
3548
|
+
line: nodeStartLine(cb),
|
|
3549
|
+
endLine: nodeEndLine(cb),
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// Events: emitter.on('event', callback)
|
|
3554
|
+
if (EVENT_METHODS.has(method)) {
|
|
3555
|
+
const eventName = findFirstStringArg(args);
|
|
3556
|
+
if (!eventName) return null;
|
|
3557
|
+
const cb = findAnonymousCallback(args);
|
|
3558
|
+
if (!cb) return null;
|
|
3559
|
+
return {
|
|
3560
|
+
name: `event:${eventName}`,
|
|
3561
|
+
kind: 'function',
|
|
3562
|
+
line: nodeStartLine(cb),
|
|
3563
|
+
endLine: nodeEndLine(cb),
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
return null;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
function extractSuperclass(heritage: TreeSitterNode): string | null {
|
|
3571
|
+
for (let i = 0; i < heritage.childCount; i++) {
|
|
3572
|
+
const child = heritage.child(i)!;
|
|
3573
|
+
if (child.type === 'identifier') return child.text;
|
|
3574
|
+
if (child.type === 'member_expression') return child.text;
|
|
3575
|
+
const found = extractSuperclass(child);
|
|
3576
|
+
if (found) return found;
|
|
3577
|
+
}
|
|
3578
|
+
return null;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
const JS_CLASS_TYPES = ['class_declaration', 'abstract_class_declaration', 'class'] as const;
|
|
3582
|
+
function findParentClass(node: TreeSitterNode): string | null {
|
|
3583
|
+
return findParentNode(node, JS_CLASS_TYPES);
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
function extractImportNames(node: TreeSitterNode): string[] {
|
|
3587
|
+
const names: string[] = [];
|
|
3588
|
+
function scan(n: TreeSitterNode): void {
|
|
3589
|
+
if (n.type === 'import_specifier' || n.type === 'export_specifier') {
|
|
3590
|
+
const nameNode = n.childForFieldName('name') || n.childForFieldName('alias');
|
|
3591
|
+
if (nameNode) names.push(nameNode.text);
|
|
3592
|
+
else names.push(n.text);
|
|
3593
|
+
} else if (n.type === 'identifier' && n.parent && n.parent.type === 'import_clause') {
|
|
3594
|
+
names.push(n.text);
|
|
3595
|
+
} else if (n.type === 'namespace_import') {
|
|
3596
|
+
names.push(n.text);
|
|
3597
|
+
}
|
|
3598
|
+
for (let i = 0; i < n.childCount; i++) scan(n.child(i)!);
|
|
3599
|
+
}
|
|
3600
|
+
scan(node);
|
|
3601
|
+
return names;
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
/**
|
|
3605
|
+
* Extract destructured names from a dynamic import() call expression.
|
|
3606
|
+
*
|
|
3607
|
+
* Handles:
|
|
3608
|
+
* const { a, b } = await import('./foo.js') → ['a', 'b']
|
|
3609
|
+
* const mod = await import('./foo.js') → ['mod']
|
|
3610
|
+
* import('./foo.js') → [] (no names extractable)
|
|
3611
|
+
*
|
|
3612
|
+
* Walks up the AST from the call_expression to find the enclosing
|
|
3613
|
+
* variable_declarator and reads the name/object_pattern.
|
|
3614
|
+
*/
|
|
3615
|
+
function extractDynamicImportNames(callNode: TreeSitterNode): string[] {
|
|
3616
|
+
// Walk up: call_expression → await_expression → variable_declarator
|
|
3617
|
+
let current = callNode.parent;
|
|
3618
|
+
// Skip await_expression wrapper if present
|
|
3619
|
+
if (current && current.type === 'await_expression') current = current.parent;
|
|
3620
|
+
// We should now be at a variable_declarator (or not, if standalone import())
|
|
3621
|
+
if (current?.type !== 'variable_declarator') return [];
|
|
3622
|
+
|
|
3623
|
+
const nameNode = current.childForFieldName('name');
|
|
3624
|
+
if (!nameNode) return [];
|
|
3625
|
+
|
|
3626
|
+
// const { a, b } = await import(...) → object_pattern
|
|
3627
|
+
if (nameNode.type === 'object_pattern') {
|
|
3628
|
+
const names: string[] = [];
|
|
3629
|
+
for (let i = 0; i < nameNode.childCount; i++) {
|
|
3630
|
+
const child = nameNode.child(i)!;
|
|
3631
|
+
if (child.type === 'shorthand_property_identifier_pattern') {
|
|
3632
|
+
names.push(child.text);
|
|
3633
|
+
} else if (child.type === 'pair_pattern') {
|
|
3634
|
+
// { a: localName } → use localName (the alias) for the local binding,
|
|
3635
|
+
// but use the key (original name) for import resolution
|
|
3636
|
+
const key = child.childForFieldName('key');
|
|
3637
|
+
if (key) names.push(key.text);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
return names;
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
// const mod = await import(...) → identifier (namespace-like import)
|
|
3644
|
+
if (nameNode.type === 'identifier') {
|
|
3645
|
+
return [nameNode.text];
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
// const [a, b] = await import(...) → array_pattern (rare but possible)
|
|
3649
|
+
if (nameNode.type === 'array_pattern') {
|
|
3650
|
+
const names: string[] = [];
|
|
3651
|
+
for (let i = 0; i < nameNode.childCount; i++) {
|
|
3652
|
+
const child = nameNode.child(i)!;
|
|
3653
|
+
if (child.type === 'identifier') names.push(child.text);
|
|
3654
|
+
else if (child.type === 'rest_pattern') {
|
|
3655
|
+
const inner = child.child(0) || child.childForFieldName('name');
|
|
3656
|
+
if (inner && inner.type === 'identifier') names.push(inner.text);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
return names;
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
return [];
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
// ── Phase 8.X: Prototype-based method extraction ────────────────────────────
|
|
3666
|
+
|
|
3667
|
+
/**
|
|
3668
|
+
* Walk the AST and extract prototype-based method definitions and aliases.
|
|
3669
|
+
*
|
|
3670
|
+
* Handles three patterns:
|
|
3671
|
+
* 1. `Foo.prototype.bar = function(){...}` — emits Foo.bar as method definition
|
|
3672
|
+
* 2. `Foo.prototype.bar = identifier` — sets typeMap['Foo.bar'] = { type: identifier }
|
|
3673
|
+
* 3. `Foo.prototype = { bar: fn, ... }` — emits defs and typeMap entries per property
|
|
3674
|
+
*
|
|
3675
|
+
* Emitting definitions under the canonical `ClassName.methodName` name lets the
|
|
3676
|
+
* existing typeMap-based call resolver find them when a typed receiver dispatches
|
|
3677
|
+
* `instance.method()` (lookup.byName('C.foo') in resolveByMethodOrGlobal).
|
|
3678
|
+
*
|
|
3679
|
+
* typeMap entries for identifier aliases (`Foo.bar → { type: 'someId' }`) are
|
|
3680
|
+
* consumed by the prototype-alias fallback added to resolveByMethodOrGlobal.
|
|
3681
|
+
*/
|
|
3682
|
+
// Prototype-method assignments (`Foo.prototype.bar = fn`) are collected inline
|
|
3683
|
+
// in runCollectorWalk's expression_statement case via handlePrototypeAssignment.
|
|
3684
|
+
|
|
3685
|
+
/**
|
|
3686
|
+
* Handle an assignment_expression that may be a prototype assignment.
|
|
3687
|
+
*
|
|
3688
|
+
* Matches:
|
|
3689
|
+
* - `Foo.prototype.bar = rhs` (lhs ends in .prototype.bar)
|
|
3690
|
+
* - `Foo.prototype = { ... }` (lhs ends in .prototype, rhs is object literal)
|
|
3691
|
+
*/
|
|
3692
|
+
function handlePrototypeAssignment(
|
|
3693
|
+
lhs: TreeSitterNode,
|
|
3694
|
+
rhs: TreeSitterNode,
|
|
3695
|
+
definitions: Definition[],
|
|
3696
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
3697
|
+
): void {
|
|
3698
|
+
if (lhs.type !== 'member_expression') return;
|
|
3699
|
+
|
|
3700
|
+
const lhsObj = lhs.childForFieldName('object');
|
|
3701
|
+
const lhsProp = lhs.childForFieldName('property');
|
|
3702
|
+
if (!lhsObj || !lhsProp) return;
|
|
3703
|
+
|
|
3704
|
+
// Pattern 1: `Foo.prototype.bar = rhs`
|
|
3705
|
+
// lhs.object is `Foo.prototype` (member_expression), lhs.property is `bar`
|
|
3706
|
+
if (
|
|
3707
|
+
lhsObj.type === 'member_expression' &&
|
|
3708
|
+
(lhsProp.type === 'property_identifier' || lhsProp.type === 'identifier')
|
|
3709
|
+
) {
|
|
3710
|
+
const protoObj = lhsObj.childForFieldName('object');
|
|
3711
|
+
const protoProp = lhsObj.childForFieldName('property');
|
|
3712
|
+
if (
|
|
3713
|
+
protoObj?.type === 'identifier' &&
|
|
3714
|
+
protoProp?.text === 'prototype' &&
|
|
3715
|
+
!BUILTIN_GLOBALS.has(protoObj.text)
|
|
3716
|
+
) {
|
|
3717
|
+
emitPrototypeMethod(protoObj.text, lhsProp.text, rhs, definitions, typeMap);
|
|
3718
|
+
}
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
// Pattern 2: `Foo.prototype = { bar: fn, ... }`
|
|
3723
|
+
// lhs.object is `Foo` (identifier), lhs.property is `prototype`
|
|
3724
|
+
if (
|
|
3725
|
+
lhsObj.type === 'identifier' &&
|
|
3726
|
+
lhsProp.text === 'prototype' &&
|
|
3727
|
+
!BUILTIN_GLOBALS.has(lhsObj.text) &&
|
|
3728
|
+
rhs.type === 'object'
|
|
3729
|
+
) {
|
|
3730
|
+
extractPrototypeObjectLiteral(lhsObj.text, rhs, definitions, typeMap);
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
/** Emit one prototype method definition or typeMap alias for `ClassName.methodName = rhs`. */
|
|
3735
|
+
function emitPrototypeMethod(
|
|
3736
|
+
className: string,
|
|
3737
|
+
methodName: string,
|
|
3738
|
+
rhs: TreeSitterNode,
|
|
3739
|
+
definitions: Definition[],
|
|
3740
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
3741
|
+
): void {
|
|
3742
|
+
const fullName = `${className}.${methodName}`;
|
|
3743
|
+
if (rhs.type === 'function_expression' || rhs.type === 'arrow_function') {
|
|
3744
|
+
const params = extractParameters(rhs);
|
|
3745
|
+
definitions.push({
|
|
3746
|
+
name: fullName,
|
|
3747
|
+
kind: 'method',
|
|
3748
|
+
line: nodeStartLine(rhs),
|
|
3749
|
+
endLine: nodeEndLine(rhs),
|
|
3750
|
+
children: params.length > 0 ? params : undefined,
|
|
3751
|
+
});
|
|
3752
|
+
} else if (rhs.type === 'identifier' && !BUILTIN_GLOBALS.has(rhs.text)) {
|
|
3753
|
+
// Prototype alias: `A.prototype.t = f` → typeMap['A.t'] = { type: 'f' }
|
|
3754
|
+
// Consumed by the prototype-alias fallback in resolveByMethodOrGlobal.
|
|
3755
|
+
setTypeMapEntry(typeMap, fullName, rhs.text, 0.9);
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
/**
|
|
3760
|
+
* Extract function-as-object property method definitions.
|
|
3761
|
+
*
|
|
3762
|
+
* Handles `fn.method = function() {}` and `fn.method = () => {}` patterns.
|
|
3763
|
+
* Emits a `method` definition named `fn.method` so that:
|
|
3764
|
+
* 1. `findCaller` attributes calls inside the body to `fn.method`
|
|
3765
|
+
* 2. `resolveByMethodOrGlobal` resolves `this.other()` inside `fn.method` to `fn.other`
|
|
3766
|
+
*
|
|
3767
|
+
* Excludes BUILTIN_GLOBALS objects and `.prototype` (handled by extractPrototypeMethodsWalk).
|
|
3768
|
+
*/
|
|
3769
|
+
// Function-as-object-property assignments (`fn.method = function(){}`) are
|
|
3770
|
+
// collected inline in runCollectorWalk's expression_statement case (walk path
|
|
3771
|
+
// only — the query path captures them via the `assign_left`/`assign_right`
|
|
3772
|
+
// query pattern in dispatchQueryMatch).
|
|
3773
|
+
|
|
3774
|
+
function handleFuncPropAssignment(
|
|
3775
|
+
lhs: TreeSitterNode,
|
|
3776
|
+
rhs: TreeSitterNode,
|
|
3777
|
+
definitions: Definition[],
|
|
3778
|
+
): void {
|
|
3779
|
+
if (lhs.type !== 'member_expression') return;
|
|
3780
|
+
if (rhs.type !== 'function_expression' && rhs.type !== 'arrow_function') return;
|
|
3781
|
+
|
|
3782
|
+
const obj = lhs.childForFieldName('object');
|
|
3783
|
+
const prop = lhs.childForFieldName('property');
|
|
3784
|
+
if (!obj || !prop) return;
|
|
3785
|
+
if (obj.type !== 'identifier') return;
|
|
3786
|
+
if (prop.type !== 'property_identifier' && prop.type !== 'identifier') return;
|
|
3787
|
+
if (BUILTIN_GLOBALS.has(obj.text)) return;
|
|
3788
|
+
if (prop.text === 'prototype') return;
|
|
3789
|
+
|
|
3790
|
+
const params = extractParameters(rhs);
|
|
3791
|
+
definitions.push({
|
|
3792
|
+
name: `${obj.text}.${prop.text}`,
|
|
3793
|
+
kind: 'method',
|
|
3794
|
+
line: nodeStartLine(rhs),
|
|
3795
|
+
endLine: nodeEndLine(rhs),
|
|
3796
|
+
children: params.length > 0 ? params : undefined,
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
/** Iterate over an object literal assigned to `Foo.prototype` and emit defs/aliases. */
|
|
3801
|
+
function extractPrototypeObjectLiteral(
|
|
3802
|
+
className: string,
|
|
3803
|
+
objNode: TreeSitterNode,
|
|
3804
|
+
definitions: Definition[],
|
|
3805
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
3806
|
+
): void {
|
|
3807
|
+
for (let i = 0; i < objNode.childCount; i++) {
|
|
3808
|
+
const child = objNode.child(i);
|
|
3809
|
+
if (!child) continue;
|
|
3810
|
+
|
|
3811
|
+
if (child.type === 'method_definition') {
|
|
3812
|
+
// Shorthand method: `Foo.prototype = { bar() {} }`
|
|
3813
|
+
const nameNode = child.childForFieldName('name');
|
|
3814
|
+
if (nameNode) {
|
|
3815
|
+
definitions.push({
|
|
3816
|
+
name: `${className}.${nameNode.text}`,
|
|
3817
|
+
kind: 'method',
|
|
3818
|
+
line: nodeStartLine(child),
|
|
3819
|
+
endLine: nodeEndLine(child),
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
continue;
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
3826
|
+
// ES6 shorthand: `Foo.prototype = { bar }` → alias typeMap['Foo.bar'] = { type: 'bar' }
|
|
3827
|
+
if (!BUILTIN_GLOBALS.has(child.text)) {
|
|
3828
|
+
setTypeMapEntry(typeMap, `${className}.${child.text}`, child.text, 0.9);
|
|
3829
|
+
}
|
|
3830
|
+
continue;
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
if (child.type !== 'pair') continue;
|
|
3834
|
+
|
|
3835
|
+
const keyNode = child.childForFieldName('key');
|
|
3836
|
+
const valueNode = child.childForFieldName('value');
|
|
3837
|
+
if (!keyNode || !valueNode) continue;
|
|
3838
|
+
|
|
3839
|
+
const methodName = keyNode.type === 'string' ? keyNode.text.replace(/['"]/g, '') : keyNode.text;
|
|
3840
|
+
if (!methodName) continue;
|
|
3841
|
+
|
|
3842
|
+
emitPrototypeMethod(className, methodName, valueNode, definitions, typeMap);
|
|
3843
|
+
}
|
|
3844
|
+
}
|