cgraphx 2.0.2 → 2.0.6
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/dist/.claude-template/FLOW-MAP.md +13 -3
- package/dist/.claude-template/agents/task-history.md +186 -0
- package/dist/.claude-template/commands/end.md +1 -0
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +5 -3
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +8 -8
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +8 -8
- package/dist/.claude-template/skills/clarify-requirements/SKILL.md +13 -9
- package/dist/.claude-template/skills/db-query/SKILL.md +19 -2
- package/dist/.claude-template/skills/iboc-check/SKILL.md +51 -23
- package/dist/.claude-template/skills/iboc-check/filler-prompt.md +3 -4
- package/dist/.claude-template/skills/run-api-test/SKILL.md +15 -15
- package/dist/.claude-template/skills/run-api-test/assets/{template-test-report.md → template-/346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212.md} +4 -4
- package/dist/.claude-template/skills/run-api-test/references/bru-run.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
- package/dist/.claude-template/skills/run-api-test/references/report-format.md +6 -6
- package/dist/.claude-template/skills/run-api-test/references/service-readiness.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/test-scope.md +6 -6
- package/dist/.claude-template/skills/run-ui-test/SKILL.md +186 -0
- package/dist/.claude-template/skills/run-ui-test/references/report-format.md +77 -0
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
- package/dist/.claude-template/skills/write-api/SKILL.md +18 -18
- package/dist/.claude-template/skills/write-api/assets/{template-api-spec.md → template-/346/216/245/345/217/243/346/265/213/350/257/225-spec.md} +1 -1
- package/dist/.claude-template/skills/write-api/references/{api-spec-format.md → /346/216/245/345/217/243/346/265/213/350/257/225-spec-format.md} +3 -3
- package/dist/.claude-template/skills/write-plan/SKILL.md +4 -2
- package/dist/.claude-template/skills/write-ui-test/SKILL.md +125 -0
- package/dist/.claude-template/skills/write-ui-test/references//347/224/250/344/276/213/350/247/204/346/240/274-format.md +94 -0
- package/dist/core/code/engine/cli/analyze-config.js +320 -0
- package/dist/core/code/engine/cli/analyze.js +1086 -0
- package/dist/core/code/engine/cli/cli-message.js +83 -0
- package/dist/core/code/engine/cli/detect-changes-format.js +58 -0
- package/dist/core/code/engine/cli/embedding-dims.js +43 -0
- package/dist/core/code/engine/cli/format-elapsed.js +12 -0
- package/dist/core/code/engine/cli/help-i18n.js +144 -0
- package/dist/core/code/engine/cli/i18n/en.js +110 -0
- package/dist/core/code/engine/cli/i18n/index.js +47 -0
- package/dist/core/code/engine/cli/i18n/resources.js +9 -0
- package/dist/core/code/engine/cli/i18n/zh-CN.js +110 -0
- package/dist/core/code/engine/cli/lazy-action.js +67 -0
- package/dist/core/code/engine/cli/optional-grammars.js +136 -0
- package/dist/core/code/engine/cli/resolve-invocation.js +76 -0
- package/dist/core/code/engine/cli/status.js +156 -0
- package/dist/core/code/engine/cli/tool.js +384 -0
- package/dist/core/code/engine/config/ignore-service.js +511 -0
- package/dist/core/code/engine/config/supported-languages.js +17 -0
- package/dist/core/code/engine/core/analysis-features.js +64 -0
- package/dist/core/code/engine/core/analyzer-identity.js +2171 -0
- package/dist/core/code/engine/core/git-staleness.js +180 -0
- package/dist/core/code/engine/core/graph/graph.js +180 -0
- package/dist/core/code/engine/core/graph/import-cycles.js +106 -0
- package/dist/core/code/engine/core/graph/types.js +2 -0
- package/dist/core/code/engine/core/index-freshness.js +12 -0
- package/dist/core/code/engine/core/ingestion/binding-accumulator.js +341 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/c-cpp.js +168 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/csharp.js +9 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/dart.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/go.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/jvm.js +54 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/php.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/python.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/ruby.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/rust.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/swift.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/typescript-javascript.js +11 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/generic.js +62 -0
- package/dist/core/code/engine/core/ingestion/call-processor.js +503 -0
- package/dist/core/code/engine/core/ingestion/call-routing.js +98 -0
- package/dist/core/code/engine/core/ingestion/call-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/cfg/callee-cell-format.js +45 -0
- package/dist/core/code/engine/core/ingestion/cfg/cfg-builder.js +202 -0
- package/dist/core/code/engine/core/ingestion/cfg/collect.js +81 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-dependence.js +185 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-flow-context.js +130 -0
- package/dist/core/code/engine/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/code/engine/core/ingestion/cfg/post-dominators.js +182 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-def-reason-codec.js +139 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs-graph.js +322 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs.js +792 -0
- package/dist/core/code/engine/core/ingestion/cfg/synthetic-escape.js +305 -0
- package/dist/core/code/engine/core/ingestion/cfg/traversal-result.js +6 -0
- package/dist/core/code/engine/core/ingestion/cfg/types.js +14 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp-harvest.js +545 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp.js +590 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/call-site-harvest.js +356 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp-harvest.js +593 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp.js +871 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart-harvest.js +874 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart.js +840 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go-harvest.js +625 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go.js +642 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java-harvest.js +517 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java.js +816 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin-harvest.js +723 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin.js +813 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php-harvest.js +630 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php.js +725 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python-harvest.js +776 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby-harvest.js +591 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby.js +760 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust-harvest.js +877 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/scope-tree-harvest.js +120 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift-harvest.js +683 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift.js +791 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript-harvest.js +1060 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript.js +587 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/c-cpp.js +77 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/csharp.js +24 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/dart.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/go.js +28 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/jvm.js +67 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/php.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/python.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/ruby.js +13 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/rust.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/swift.js +21 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/typescript-javascript.js +31 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/generic.js +144 -0
- package/dist/core/code/engine/core/ingestion/class-types.js +2 -0
- package/dist/core/code/engine/core/ingestion/cluster-enricher.js +174 -0
- package/dist/core/code/engine/core/ingestion/community-processor.js +604 -0
- package/dist/core/code/engine/core/ingestion/constants.js +26 -0
- package/dist/core/code/engine/core/ingestion/cpp-ue-preprocessor.js +263 -0
- package/dist/core/code/engine/core/ingestion/csharp-namespace-gate.js +133 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/index.js +38 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/spring.js +310 -0
- package/dist/core/code/engine/core/ingestion/emit-references.js +244 -0
- package/dist/core/code/engine/core/ingestion/entry-point-scoring.js +201 -0
- package/dist/core/code/engine/core/ingestion/export-detection.js +244 -0
- package/dist/core/code/engine/core/ingestion/field-extractor.js +29 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/c-cpp.js +107 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/csharp.js +124 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/dart.js +99 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/go.js +102 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/helpers.js +198 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/jvm.js +172 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/python.js +94 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/ruby.js +79 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/swift.js +93 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/typescript-javascript.js +59 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/generic.js +147 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/typescript.js +266 -0
- package/dist/core/code/engine/core/ingestion/field-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/filesystem-walker.js +136 -0
- package/dist/core/code/engine/core/ingestion/finalize-orchestrator.js +159 -0
- package/dist/core/code/engine/core/ingestion/framework-detection.js +432 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/analysis-features.js +38 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/annotation-arguments.js +234 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop-candidates.js +88 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop.js +487 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/auto-configuration.js +21 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-candidates.js +189 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-catalog.js +32 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-factories.js +73 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/conditionals.js +323 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/config-bindings.js +119 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/di-metadata.js +385 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/resource-injection.js +96 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/c-cpp.js +17 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/csharp.js +46 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/dart.js +59 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/go.js +30 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/jvm.js +73 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/php.js +19 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/python.js +45 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/rust.js +58 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/swift.js +94 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/typescript-javascript.js +26 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/csharp.js +128 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/go.js +50 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/jvm.js +112 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/python.js +75 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/resolver-factory.js +36 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/standard.js +180 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/types.js +7 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/utils.js +153 -0
- package/dist/core/code/engine/core/ingestion/import-target-adapter.js +99 -0
- package/dist/core/code/engine/core/ingestion/language-config.js +391 -0
- package/dist/core/code/engine/core/ingestion/language-provider.js +25 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity-metadata.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/c/capture-side-channel.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/c/captures.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/c/header-scan.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-decomposer.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/c/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/c/interpret.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/c/merge-bindings.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/c/query.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/c/scope-resolver.js +112 -0
- package/dist/core/code/engine/core/ingestion/languages/c/simple-hooks.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/c/static-linkage.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/c-cpp.js +506 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/adl.js +804 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity-metadata.js +258 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/capture-side-channel.js +89 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/captures.js +1975 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-extractor.js +311 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-filter.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/conversion-rank.js +163 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/file-local-linkage.js +327 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/header-scan.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-decomposer.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-target.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/inline-namespaces.js +379 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/interpret.js +239 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/member-lookup.js +470 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/merge-bindings.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/query.js +763 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/range-bindings.js +230 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/scope-resolver.js +354 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/simple-hooks.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/two-phase-lookup.js +348 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/type-classifier.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/user-defined-conversions.js +128 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/accessor-unwrap.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/captures.js +557 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-decomposer.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-target.js +176 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/index.js +95 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/interpret.js +150 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/merge-bindings.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/namespace-siblings.js +708 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/qualified-type-names.js +62 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/query.js +578 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/receiver-binding.js +142 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/resolution-config.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/scope-resolver.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/simple-hooks.js +81 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp.js +204 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity-metadata.js +38 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/built-ins.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/cache-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/captures.js +1096 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/expand-wildcards.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/extension-type-preprocess.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/import-target.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/index.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/interpret.js +101 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/merge-bindings.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/query.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/receiver-binding.js +90 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/scope-resolver.js +197 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/signature-bindings.js +54 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/simple-hooks.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/dart.js +138 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity-metadata.js +71 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity.js +17 -0
- package/dist/core/code/engine/core/ingestion/languages/go/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/captures.js +492 -0
- package/dist/core/code/engine/core/ingestion/languages/go/expand-wildcards.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/go/generic-type-parameters.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-decomposer.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-target.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/go/index.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interface-impls.js +955 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interpret.js +177 -0
- package/dist/core/code/engine/core/ingestion/languages/go/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/method-owners.js +131 -0
- package/dist/core/code/engine/core/ingestion/languages/go/namespace-mirror.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-clause.js +79 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-siblings.js +83 -0
- package/dist/core/code/engine/core/ingestion/languages/go/query.js +298 -0
- package/dist/core/code/engine/core/ingestion/languages/go/range-binding.js +127 -0
- package/dist/core/code/engine/core/ingestion/languages/go/receiver-binding.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/go/scope-resolver.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/go/simple-hooks.js +31 -0
- package/dist/core/code/engine/core/ingestion/languages/go/type-binding.js +279 -0
- package/dist/core/code/engine/core/ingestion/languages/go.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/index.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/java/analysis-features.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity-metadata.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity.js +27 -0
- package/dist/core/code/engine/core/ingestion/languages/java/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/java/capture-side-channel.js +123 -0
- package/dist/core/code/engine/core/ingestion/languages/java/captures.js +791 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-decomposer.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/java/index.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/interpret.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/java/merge-bindings.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/query.js +319 -0
- package/dist/core/code/engine/core/ingestion/languages/java/receiver-binding.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/java/scope-resolver.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/java/simple-hooks.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-aop.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-conditionals.js +52 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-config-bindings.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-di.js +165 -0
- package/dist/core/code/engine/core/ingestion/languages/java.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/arity.js +15 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/captures.js +1122 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/import-target.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/index.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/interpret.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/query.js +659 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/scope-resolver.js +78 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/simple-hooks.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-facts.js +46 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-siblings.js +200 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity-metadata.js +23 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/capture-side-channel.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/captures.js +1262 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/companion-scopes.js +72 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-decomposer.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-target.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/index.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/interpret.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/merge-bindings.js +28 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/owners.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/query.js +243 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/receiver-binding.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/scope-resolver.js +207 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/simple-hooks.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-aop.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-conditionals.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-di.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity-metadata.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/php/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/php/captures.js +1166 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-decomposer.js +238 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-target.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/php/index.js +85 -0
- package/dist/core/code/engine/core/ingestion/languages/php/interpret.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/php/merge-bindings.js +50 -0
- package/dist/core/code/engine/core/ingestion/languages/php/namespace-siblings.js +353 -0
- package/dist/core/code/engine/core/ingestion/languages/php/query.js +391 -0
- package/dist/core/code/engine/core/ingestion/languages/php/receiver-binding.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/php/scope-resolver.js +361 -0
- package/dist/core/code/engine/core/ingestion/languages/php/simple-hooks.js +116 -0
- package/dist/core/code/engine/core/ingestion/languages/php.js +302 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/python/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/python/captures.js +299 -0
- package/dist/core/code/engine/core/ingestion/languages/python/depends-references.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-decomposer.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-target.js +440 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/python/interpret.js +430 -0
- package/dist/core/code/engine/core/ingestion/languages/python/merge-bindings.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/python/query.js +323 -0
- package/dist/core/code/engine/core/ingestion/languages/python/receiver-binding.js +310 -0
- package/dist/core/code/engine/core/ingestion/languages/python/scope-resolver.js +80 -0
- package/dist/core/code/engine/core/ingestion/languages/python/simple-hooks.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/python.js +140 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/captures.js +864 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/import-target.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/interpret.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/query.js +349 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/receiver-binding.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/scope-resolver.js +263 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/simple-hooks.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby.js +215 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/arity.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/captures.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-decomposer.js +167 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-target.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/interpret.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/method-owners.js +76 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/module-path.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/qualified-call.js +482 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/query.js +280 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/range-binding.js +687 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/receiver-binding.js +148 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/scope-resolver.js +151 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/simple-hooks.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/rust.js +183 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity-metadata.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/base-type.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/captures.js +594 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/conditional-directive-preprocess.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/implicit-imports.js +60 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-decomposer.js +87 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-target.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/index.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/interpret.js +93 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/merge-bindings.js +51 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/query.js +226 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/receiver-binding.js +169 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/scope-resolver.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/sibling-type-bindings.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/signature-bindings.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/simple-hooks.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-grouping.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-siblings.js +74 -0
- package/dist/core/code/engine/core/ingestion/languages/swift.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity-metadata.js +106 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity.js +57 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/array-callback.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/captures.js +956 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-export-assignment.js +535 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-module-exports.js +196 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-decomposer.js +374 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-target.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/index.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/interpret.js +344 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/merge-bindings.js +161 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/nuxt-auto-imports.js +325 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/query.js +1328 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/receiver-binding.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/scope-resolver.js +293 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/simple-hooks.js +139 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript.js +455 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/captures.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/import-target.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/index.js +55 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/scope-resolver.js +295 -0
- package/dist/core/code/engine/core/ingestion/languages/vue.js +96 -0
- package/dist/core/code/engine/core/ingestion/local-symbol-pruner.js +68 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/csharp.js +290 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/dart.js +392 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/go.js +179 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/jvm.js +350 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/php.js +306 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/python.js +312 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/ruby.js +289 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/rust.js +198 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/swift.js +286 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/typescript-javascript.js +341 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/generic.js +209 -0
- package/dist/core/code/engine/core/ingestion/method-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/model/field-registry.js +41 -0
- package/dist/core/code/engine/core/ingestion/model/index.js +52 -0
- package/dist/core/code/engine/core/ingestion/model/method-registry.js +138 -0
- package/dist/core/code/engine/core/ingestion/model/owned-members-lookup.js +46 -0
- package/dist/core/code/engine/core/ingestion/model/registration-table.js +234 -0
- package/dist/core/code/engine/core/ingestion/model/resolve.js +183 -0
- package/dist/core/code/engine/core/ingestion/model/scope-resolution-indexes.js +43 -0
- package/dist/core/code/engine/core/ingestion/model/semantic-model.js +179 -0
- package/dist/core/code/engine/core/ingestion/model/symbol-table.js +216 -0
- package/dist/core/code/engine/core/ingestion/model/type-registry.js +84 -0
- package/dist/core/code/engine/core/ingestion/mro-processor.js +709 -0
- package/dist/core/code/engine/core/ingestion/parsing-processor.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/communities.js +69 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/cross-file.js +71 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/di.js +338 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/http-api-calls.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/index.js +54 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/mro.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/orm.js +78 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse-impl.js +1286 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse.js +41 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/processes.js +193 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/prune-local-symbols.js +29 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/registry.js +52 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/routes.js +409 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/rpc-edges.js +301 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/runner.js +207 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/scan.js +49 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-aop.js +442 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-auto-configuration.js +264 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-config.js +440 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/structure.js +38 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/tools.js +89 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/types.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline.js +152 -0
- package/dist/core/code/engine/core/ingestion/process-processor.js +325 -0
- package/dist/core/code/engine/core/ingestion/resolve-references.js +205 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/constant-resolver.js +135 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django-root-discovery.js +221 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django.js +428 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/expo.js +39 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/fastapi-router-bindings.js +264 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/laravel.js +501 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/middleware.js +175 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/nextjs.js +81 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/php.js +25 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/python-const-resolver.js +307 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/response-shapes.js +299 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/route-path.js +71 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring-shared.js +310 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor-bridge.js +60 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor.js +1373 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/contract/scope-resolver.js +282 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +72 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/edges.js +194 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/ids.js +472 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +43 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +274 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +93 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/callable-value-flow.js +1240 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/compound-receiver.js +1174 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/free-call-fallback.js +873 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/imported-return-types.js +226 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/mro.js +107 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/overload-narrowing.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/property-dispatch.js +122 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1720 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/phase.js +395 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +208 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/registry.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/run.js +632 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +112 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/resolution-outcome.js +41 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/namespace-targets.js +81 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/walkers.js +1835 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/unresolved-receivers.js +242 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/utils/definition-id.js +22 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/workspace-index.js +151 -0
- package/dist/core/code/engine/core/ingestion/structure-processor.js +40 -0
- package/dist/core/code/engine/core/ingestion/tree-sitter-queries.js +2244 -0
- package/dist/core/code/engine/core/ingestion/ts-js-hoc-utils.js +115 -0
- package/dist/core/code/engine/core/ingestion/type-env.js +1136 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/c-cpp.js +555 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/csharp.js +570 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/dart.js +372 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/go.js +508 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/jvm.js +875 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/php.js +537 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/python.js +477 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/ruby.js +380 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/rust.js +502 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/shared.js +843 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/swift.js +490 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/types.js +2 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/typescript.js +690 -0
- package/dist/core/code/engine/core/ingestion/utils/ast-helpers.js +1693 -0
- package/dist/core/code/engine/core/ingestion/utils/call-analysis.js +779 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-flow-captures.js +932 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-labels.js +49 -0
- package/dist/core/code/engine/core/ingestion/utils/deferred-resolution-profile.js +151 -0
- package/dist/core/code/engine/core/ingestion/utils/effective-ram.js +67 -0
- package/dist/core/code/engine/core/ingestion/utils/env.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/event-loop.js +9 -0
- package/dist/core/code/engine/core/ingestion/utils/graph-sort.js +103 -0
- package/dist/core/code/engine/core/ingestion/utils/heap-probe.js +45 -0
- package/dist/core/code/engine/core/ingestion/utils/heritage-marker.js +47 -0
- package/dist/core/code/engine/core/ingestion/utils/line-base.js +24 -0
- package/dist/core/code/engine/core/ingestion/utils/max-file-size.js +59 -0
- package/dist/core/code/engine/core/ingestion/utils/method-props.js +198 -0
- package/dist/core/code/engine/core/ingestion/utils/qualified-name.js +73 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-captures.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-codec.js +188 -0
- package/dist/core/code/engine/core/ingestion/utils/scope-tree-walk.js +36 -0
- package/dist/core/code/engine/core/ingestion/utils/symbol-labels.js +48 -0
- package/dist/core/code/engine/core/ingestion/utils/template-arguments.js +187 -0
- package/dist/core/code/engine/core/ingestion/utils/type-parameters.js +209 -0
- package/dist/core/code/engine/core/ingestion/utils/verbose.js +6 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/c-cpp.js +133 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/csharp.js +66 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/dart.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/go.js +153 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/jvm.js +145 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/php.js +61 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/python.js +104 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/ruby.js +55 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/swift.js +91 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/typescript-javascript.js +86 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/generic.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/vue-sfc-extractor.js +544 -0
- package/dist/core/code/engine/core/ingestion/workers/callable-id.js +122 -0
- package/dist/core/code/engine/core/ingestion/workers/clone-safety.js +470 -0
- package/dist/core/code/engine/core/ingestion/workers/parse-worker.js +2456 -0
- package/dist/core/code/engine/core/ingestion/workers/post-result.js +90 -0
- package/dist/core/code/engine/core/ingestion/workers/quarantine.js +41 -0
- package/dist/core/code/engine/core/ingestion/workers/result-merge.js +59 -0
- package/dist/core/code/engine/core/ingestion/workers/worker-pool.js +1725 -0
- package/dist/core/code/engine/core/ingestion/workspace-config.js +139 -0
- package/dist/core/code/engine/core/lbug/conn-lock.js +72 -0
- package/dist/core/code/engine/core/lbug/csv-generator.js +710 -0
- package/dist/core/code/engine/core/lbug/cypher-escape.js +24 -0
- package/dist/core/code/engine/core/lbug/extension-load-error.js +339 -0
- package/dist/core/code/engine/core/lbug/extension-loader.js +261 -0
- package/dist/core/code/engine/core/lbug/graph-emit-sink.js +584 -0
- package/dist/core/code/engine/core/lbug/lbug-adapter.js +2703 -0
- package/dist/core/code/engine/core/lbug/lbug-config.js +1029 -0
- package/dist/core/code/engine/core/lbug/native-check.js +435 -0
- package/dist/core/code/engine/core/lbug/pool-adapter.js +973 -0
- package/dist/core/code/engine/core/lbug/query-params.js +25 -0
- package/dist/core/code/engine/core/lbug/query-result-utils.js +31 -0
- package/dist/core/code/engine/core/lbug/rel-pair-routing.js +373 -0
- package/dist/core/code/engine/core/lbug/schema.js +771 -0
- package/dist/core/code/engine/core/lbug/shutdown-helpers.js +40 -0
- package/dist/core/code/engine/core/lbug/sidecar-recovery.js +716 -0
- package/dist/core/code/engine/core/lbug/stdio-capture.js +49 -0
- package/dist/core/code/engine/core/lbug/sync-csv-writer.js +115 -0
- package/dist/core/code/engine/core/lbug/wal-checkpoint-driver.js +215 -0
- package/dist/core/code/engine/core/lbug/wal-driver-state.js +28 -0
- package/dist/core/code/engine/core/logger.js +339 -0
- package/dist/core/code/engine/core/platform/capabilities.js +91 -0
- package/dist/core/code/engine/core/run-analyze.js +1098 -0
- package/dist/core/code/engine/core/tree-sitter/parser-loader.js +281 -0
- package/dist/core/code/engine/core/tree-sitter/safe-parse.js +258 -0
- package/dist/core/code/engine/core/tree-sitter/vendored-grammars.js +64 -0
- package/dist/core/code/engine/lib/utils.js +121 -0
- package/dist/core/code/engine/mcp/core/lbug-adapter.js +27 -0
- package/dist/core/code/engine/mcp/local/aop-metadata.js +230 -0
- package/dist/core/code/engine/mcp/local/bean-metadata.js +49 -0
- package/dist/core/code/engine/mcp/local/limits.js +15 -0
- package/dist/core/code/engine/mcp/local/line-display.js +6 -0
- package/dist/core/code/engine/mcp/local/local-backend.js +4142 -0
- package/dist/core/code/engine/storage/branch-index.js +72 -0
- package/dist/core/code/engine/storage/file-hash.js +95 -0
- package/dist/core/code/engine/storage/fs-atomic.js +34 -0
- package/dist/core/code/engine/storage/git.js +555 -0
- package/dist/core/code/engine/storage/index-lock.js +664 -0
- package/dist/core/code/engine/storage/parse-cache.js +650 -0
- package/dist/core/code/engine/storage/parsedfile-store.js +620 -0
- package/dist/core/code/engine/storage/repo-manager.js +1061 -0
- package/dist/core/code/engine/storage/scope-index-store.js +247 -0
- package/dist/core/code/engine/types/pipeline.js +2 -0
- package/dist/core/code/scripts/install-duckdb-extension.mjs +125 -0
- package/dist/core/code/scripts/resolve-analyze-cmd.cjs +346 -0
- package/dist/core/code/shared/graph/types.js +8 -0
- package/dist/core/code/shared/index.js +105 -0
- package/dist/core/code/shared/integrations/circuit-breaker.js +242 -0
- package/dist/core/code/shared/integrations/resilient-fetch.js +224 -0
- package/dist/core/code/shared/integrations/retry.js +70 -0
- package/dist/core/code/shared/integrations/understand-quickly.js +145 -0
- package/dist/core/code/shared/language-detection.js +162 -0
- package/dist/core/code/shared/languages.js +27 -0
- package/dist/core/code/shared/lbug/schema-constants.js +98 -0
- package/dist/core/code/shared/mro-strategy.js +2 -0
- package/dist/core/code/shared/pipeline.js +5 -0
- package/dist/core/code/shared/scope-resolution/callable-flow-site.js +11 -0
- package/dist/core/code/shared/scope-resolution/def-index.js +53 -0
- package/dist/core/code/shared/scope-resolution/evidence-weights.js +87 -0
- package/dist/core/code/shared/scope-resolution/finalize-algorithm.js +807 -0
- package/dist/core/code/shared/scope-resolution/language-classification.js +46 -0
- package/dist/core/code/shared/scope-resolution/method-dispatch-index.js +100 -0
- package/dist/core/code/shared/scope-resolution/module-scope-index.js +59 -0
- package/dist/core/code/shared/scope-resolution/origin-priority.js +23 -0
- package/dist/core/code/shared/scope-resolution/parsed-file.js +54 -0
- package/dist/core/code/shared/scope-resolution/position-index.js +136 -0
- package/dist/core/code/shared/scope-resolution/qualified-name-index.js +77 -0
- package/dist/core/code/shared/scope-resolution/reference-site.js +24 -0
- package/dist/core/code/shared/scope-resolution/registries/class-registry.js +32 -0
- package/dist/core/code/shared/scope-resolution/registries/context.js +52 -0
- package/dist/core/code/shared/scope-resolution/registries/evidence.js +152 -0
- package/dist/core/code/shared/scope-resolution/registries/field-registry.js +33 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-core.js +392 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-qualified.js +58 -0
- package/dist/core/code/shared/scope-resolution/registries/macro-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/method-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/tie-breaks.js +63 -0
- package/dist/core/code/shared/scope-resolution/resolve-type-ref.js +128 -0
- package/dist/core/code/shared/scope-resolution/scope-id.js +49 -0
- package/dist/core/code/shared/scope-resolution/scope-tree.js +225 -0
- package/dist/core/code/shared/scope-resolution/symbol-definition.js +12 -0
- package/dist/core/code/shared/scope-resolution/types.js +25 -0
- package/dist/core/code/shared/test-helpers.js +17 -0
- package/dist/core/code/vendor/leiden/index.cjs +355 -0
- package/dist/core/code/vendor/leiden/utils.cjs +419 -0
- package/dist/core/dbquery/cli.d.ts.map +1 -1
- package/dist/core/dbquery/cli.js +5 -4
- package/dist/core/dbquery/cli.js.map +1 -1
- package/dist/core/dbquery/config.d.ts.map +1 -1
- package/dist/core/dbquery/config.js +39 -3
- package/dist/core/dbquery/config.js.map +1 -1
- package/dist/core/dbquery/drivers/http.d.ts +23 -0
- package/dist/core/dbquery/drivers/http.d.ts.map +1 -0
- package/dist/core/dbquery/drivers/http.js +125 -0
- package/dist/core/dbquery/drivers/http.js.map +1 -0
- package/dist/core/dbquery/drivers/mysql.js +1 -1
- package/dist/core/dbquery/drivers/mysql.js.map +1 -1
- package/dist/core/dbquery/drivers/postgres.js +1 -1
- package/dist/core/dbquery/drivers/postgres.js.map +1 -1
- package/dist/core/dbquery/executor.d.ts.map +1 -1
- package/dist/core/dbquery/executor.js +46 -12
- package/dist/core/dbquery/executor.js.map +1 -1
- package/dist/core/dbquery/format.d.ts.map +1 -1
- package/dist/core/dbquery/format.js +2 -1
- package/dist/core/dbquery/format.js.map +1 -1
- package/dist/core/dbquery/init.d.ts.map +1 -1
- package/dist/core/dbquery/init.js +7 -0
- package/dist/core/dbquery/init.js.map +1 -1
- package/dist/core/dbquery/types.d.ts +55 -7
- package/dist/core/dbquery/types.d.ts.map +1 -1
- package/dist/core/directory.d.ts +17 -0
- package/dist/core/directory.d.ts.map +1 -1
- package/dist/core/directory.js +37 -0
- package/dist/core/directory.js.map +1 -1
- package/dist/core/features/cli.d.ts.map +1 -1
- package/dist/core/features/cli.js +3 -2
- package/dist/core/features/cli.js.map +1 -1
- package/dist/core/features/scanner.d.ts +1 -1
- package/dist/core/features/scanner.d.ts.map +1 -1
- package/dist/core/features/scanner.js +20 -4
- package/dist/core/features/scanner.js.map +1 -1
- package/dist/core/markdown/cli.d.ts.map +1 -1
- package/dist/core/markdown/cli.js +9 -8
- package/dist/core/markdown/cli.js.map +1 -1
- package/dist/core/timeline/cli.d.ts.map +1 -1
- package/dist/core/timeline/cli.js +13 -6
- package/dist/core/timeline/cli.js.map +1 -1
- package/dist/core/timeline/debris.d.ts +20 -0
- package/dist/core/timeline/debris.d.ts.map +1 -0
- package/dist/core/timeline/debris.js +124 -0
- package/dist/core/timeline/debris.js.map +1 -0
- package/dist/core/timeline/hook-runner.d.ts.map +1 -1
- package/dist/core/timeline/hook-runner.js +3 -1
- package/dist/core/timeline/hook-runner.js.map +1 -1
- package/dist/core/timeline/hooks.d.ts.map +1 -1
- package/dist/core/timeline/hooks.js +13 -5
- package/dist/core/timeline/hooks.js.map +1 -1
- package/dist/core/timeline/installer.d.ts +2 -0
- package/dist/core/timeline/installer.d.ts.map +1 -1
- package/dist/core/timeline/installer.js +12 -0
- package/dist/core/timeline/installer.js.map +1 -1
- package/dist/core/timeline/project-root.d.ts +21 -0
- package/dist/core/timeline/project-root.d.ts.map +1 -0
- package/dist/core/timeline/project-root.js +83 -0
- package/dist/core/timeline/project-root.js.map +1 -0
- package/dist/web/register.d.ts.map +1 -1
- package/dist/web/register.js +2 -1
- package/dist/web/register.js.map +1 -1
- package/package.json +1 -1
- package/dist/.claude-template/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-deck/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-deck/references/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-sketch-engine/.cgraphx/timeline-current-turn.json +0 -1
- /package/dist/.claude-template/skills/run-api-test/assets/{template-test-verify.jsonl → template-/346/216/245/345/217/243/346/265/213/350/257/225/351/252/214/350/257/201.jsonl"} +0 -0
|
@@ -0,0 +1,2456 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extractORMQueries = extractORMQueries;
|
|
7
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
8
|
+
const callable_id_js_1 = require("./callable-id.js");
|
|
9
|
+
const tree_sitter_1 = __importDefault(require("tree-sitter"));
|
|
10
|
+
const node_module_1 = require("node:module");
|
|
11
|
+
const tree_sitter_javascript_1 = __importDefault(require("tree-sitter-javascript"));
|
|
12
|
+
const tree_sitter_typescript_1 = __importDefault(require("tree-sitter-typescript"));
|
|
13
|
+
const tree_sitter_java_1 = __importDefault(require("tree-sitter-java"));
|
|
14
|
+
const vendored_grammars_js_1 = require("../../tree-sitter/vendored-grammars.js");
|
|
15
|
+
const index_js_1 = require("../../../../shared/index.js");
|
|
16
|
+
const index_js_2 = require("../languages/index.js");
|
|
17
|
+
const constants_js_1 = require("../constants.js");
|
|
18
|
+
const ts_js_hoc_utils_js_1 = require("../ts-js-hoc-utils.js");
|
|
19
|
+
const safe_parse_js_1 = require("../../tree-sitter/safe-parse.js");
|
|
20
|
+
const post_result_js_1 = require("./post-result.js");
|
|
21
|
+
const result_merge_js_1 = require("./result-merge.js");
|
|
22
|
+
// ── Worker grammar loading — enforcement boundary (#2091/#2093, #2101) ───────
|
|
23
|
+
// The worker maintains its own grammar table (the guarded vendored-grammar
|
|
24
|
+
// loads below + `languageMap`) and intentionally does NOT consult the runtime
|
|
25
|
+
// `CGRAPH_SKIP_OPTIONAL_GRAMMARS` opt-out. It does not need to: the MAIN
|
|
26
|
+
// THREAD's `parseableScanned` filter (pipeline-phases/parse-impl.ts, gated on
|
|
27
|
+
// `parser-loader.isLanguageAvailable`, which honors the runtime opt-out and a
|
|
28
|
+
// genuinely-absent binding alike) excludes files of an unavailable/opted-out
|
|
29
|
+
// language BEFORE any chunk is dispatched, so the worker never receives them.
|
|
30
|
+
// That main-thread filter is the single enforcement point. Any future change
|
|
31
|
+
// that dispatches files to the worker WITHOUT first passing them through
|
|
32
|
+
// `isLanguageAvailable` must re-introduce the gate here. (The cleaner end-state
|
|
33
|
+
// — routing this table through `parser-loader.getLanguageGrammar` so there is
|
|
34
|
+
// one loader — is the deferred Tier-1 consolidation.)
|
|
35
|
+
// Swift/Dart/Kotlin/C are vendored grammars loaded from `vendor/` by absolute
|
|
36
|
+
// path (NEVER copied into node_modules — see vendored-grammars.ts / #2111). Each
|
|
37
|
+
// may be absent on a platform without a prebuild or a toolchain-less /
|
|
38
|
+
// `--ignore-scripts` install, so every load is guarded so a missing binding
|
|
39
|
+
// cannot crash the worker at module-load (#2091/#2093, #2116).
|
|
40
|
+
let Swift = null;
|
|
41
|
+
try {
|
|
42
|
+
Swift = (0, vendored_grammars_js_1.requireVendoredGrammar)('tree-sitter-swift');
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
let Dart = null;
|
|
46
|
+
try {
|
|
47
|
+
Dart = (0, vendored_grammars_js_1.requireVendoredGrammar)('tree-sitter-dart');
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
let Kotlin = null;
|
|
51
|
+
try {
|
|
52
|
+
Kotlin = (0, vendored_grammars_js_1.requireVendoredGrammar)('tree-sitter-kotlin');
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
55
|
+
let C = null;
|
|
56
|
+
try {
|
|
57
|
+
C = (0, vendored_grammars_js_1.requireVendoredGrammar)('tree-sitter-c');
|
|
58
|
+
}
|
|
59
|
+
catch { }
|
|
60
|
+
// ── 非 v1 语法包懒加载(spec §7/§13)─────────────────────────────────────────
|
|
61
|
+
// v1 仅 java/js/ts 三个语法包随生产依赖分发;python/csharp/cpp/go/rust/php/ruby
|
|
62
|
+
// 7 个包移至 devDependencies,生产 --omit=dev 不装。worker 首次用到某语言时按需
|
|
63
|
+
// require 并缓存:成功 → 语法对象;失败(MODULE_NOT_FOUND)→ null → 该语言降级为
|
|
64
|
+
// "不解析,文件按结构层收录"(spec §7 末条),不阻断 analyze。开发/测试环境 devDeps
|
|
65
|
+
// 装齐 10 个语法包,行为与原静态 import 完全一致。守护模式参照 parser-loader.ts
|
|
66
|
+
// 的 requireVendoredGrammar / _require(createRequire + 缓存 + try/catch)。
|
|
67
|
+
const _require = (0, node_module_1.createRequire)(__filename);
|
|
68
|
+
const NON_V1_GRAMMAR_LOADERS = {
|
|
69
|
+
[index_js_1.SupportedLanguages.Python]: () => _require('tree-sitter-python'),
|
|
70
|
+
[index_js_1.SupportedLanguages.CPlusPlus]: () => _require('tree-sitter-cpp'),
|
|
71
|
+
// 显式 subpath 导入 — 理由见 parser-loader.ts(#1013)。
|
|
72
|
+
[index_js_1.SupportedLanguages.CSharp]: () => _require('tree-sitter-c-sharp/bindings/node/index.js'),
|
|
73
|
+
[index_js_1.SupportedLanguages.Go]: () => _require('tree-sitter-go'),
|
|
74
|
+
[index_js_1.SupportedLanguages.Rust]: () => _require('tree-sitter-rust'),
|
|
75
|
+
[index_js_1.SupportedLanguages.PHP]: () => _require('tree-sitter-php').php_only,
|
|
76
|
+
[index_js_1.SupportedLanguages.Ruby]: () => _require('tree-sitter-ruby'),
|
|
77
|
+
};
|
|
78
|
+
const lazyGrammarCache = new Map();
|
|
79
|
+
const loadNonV1Grammar = (key) => {
|
|
80
|
+
const cached = lazyGrammarCache.get(key);
|
|
81
|
+
if (cached !== undefined)
|
|
82
|
+
return cached;
|
|
83
|
+
const loader = NON_V1_GRAMMAR_LOADERS[key];
|
|
84
|
+
let result = null;
|
|
85
|
+
if (loader) {
|
|
86
|
+
try {
|
|
87
|
+
result = loader();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// 包未装(生产 --omit=dev)或原生绑定损坏 → 降级为不解析,不抛错。
|
|
91
|
+
result = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
lazyGrammarCache.set(key, result);
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
const index_js_3 = require("../../../../shared/index.js");
|
|
98
|
+
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
99
|
+
const callable_labels_js_1 = require("../utils/callable-labels.js");
|
|
100
|
+
const call_analysis_js_1 = require("../utils/call-analysis.js");
|
|
101
|
+
const type_env_js_1 = require("../type-env.js");
|
|
102
|
+
const framework_detection_js_1 = require("../framework-detection.js");
|
|
103
|
+
const utils_js_1 = require("../../../lib/utils.js");
|
|
104
|
+
const cjs_export_assignment_js_1 = require("../languages/typescript/cjs-export-assignment.js");
|
|
105
|
+
const vue_sfc_extractor_js_1 = require("../vue-sfc-extractor.js");
|
|
106
|
+
const method_props_js_1 = require("../utils/method-props.js");
|
|
107
|
+
const template_arguments_js_1 = require("../utils/template-arguments.js");
|
|
108
|
+
const scope_extractor_bridge_js_1 = require("../scope-extractor-bridge.js");
|
|
109
|
+
const parsedfile_store_js_1 = require("../../../storage/parsedfile-store.js");
|
|
110
|
+
const laravel_js_1 = require("../route-extractors/laravel.js");
|
|
111
|
+
const collect_js_1 = require("../cfg/collect.js");
|
|
112
|
+
const logger_js_1 = require("../../logger.js");
|
|
113
|
+
// ── ParsedFile store (#1983 parallel serialization) ─────────────────────────
|
|
114
|
+
// Read ONCE at worker init from `workerData` (immutable for the run, inherited
|
|
115
|
+
// by respawned workers via the pool's factory closure). When set, this worker
|
|
116
|
+
// writes its own ParsedFile shards to disk at each job flush instead of
|
|
117
|
+
// returning them over the MessageChannel — parallelizing serialization off the
|
|
118
|
+
// main thread. `undefined` ⇒ return ParsedFiles in the result (no-store
|
|
119
|
+
// fallback). `shardSeq` makes each shard name unique within this worker; global
|
|
120
|
+
// uniqueness for the run rests on the process-monotonic `threadId` (never reused
|
|
121
|
+
// across respawns) plus the per-run store clear on the main thread.
|
|
122
|
+
const PARSED_FILE_STORE_STORAGE_PATH = node_worker_threads_1.workerData?.parsedFileStoreStoragePath;
|
|
123
|
+
// Durable, content-addressed ParsedFile store dir (#2038 warm-cache coverage).
|
|
124
|
+
// When set AND the flush carries a chunk hash, the worker ALSO writes its
|
|
125
|
+
// ParsedFiles to `<durableDir>/<chunkHash>/` so a future warm parse-cache hit
|
|
126
|
+
// restores them without re-parsing. `undefined` ⇒ no durable write.
|
|
127
|
+
const DURABLE_PARSED_FILE_STORAGE_PATH = node_worker_threads_1.workerData?.durableParsedFileStoragePath;
|
|
128
|
+
let shardSeq = 0;
|
|
129
|
+
// ── PDG/CFG opt-in (#2081 M1) ───────────────────────────────────────────────
|
|
130
|
+
// Read ONCE at worker init from `workerData` (the worker never sees
|
|
131
|
+
// PipelineOptions — config arrives via the pool factory's `workerData`, see
|
|
132
|
+
// KTD7 / U5). When `pdg` is set, the worker builds a per-function control-flow
|
|
133
|
+
// graph from the tree-sitter AST (where it lives) and serializes it onto
|
|
134
|
+
// `ParsedFile.cfgSideChannel`. Off ⇒ no CFG work and no field — the default for
|
|
135
|
+
// every run today. `pdgMaxFunctionLines` bounds per-function CFG cost
|
|
136
|
+
// (0/undefined ⇒ no cap; see collectFunctionCfgs).
|
|
137
|
+
const PDG_ENABLED = node_worker_threads_1.workerData?.pdg === true;
|
|
138
|
+
const PDG_MAX_FUNCTION_LINES = node_worker_threads_1.workerData?.pdgMaxFunctionLines ??
|
|
139
|
+
collect_js_1.DEFAULT_PDG_MAX_FUNCTION_LINES;
|
|
140
|
+
// ── Bootstrap-stage diagnostics (#1741) ────────────────────────────────────
|
|
141
|
+
// When CGRAPH_WORKER_BOOTSTRAP=1 (or --verbose sets CGRAPH_VERBOSE), each
|
|
142
|
+
// worker reports its startup stage timings to stderr — which the pool tees
|
|
143
|
+
// and captures (worker-pool.ts captureWorkerStderr). This makes a slow or
|
|
144
|
+
// crashing startup diagnosable: you can see whether a worker reached
|
|
145
|
+
// "grammars loaded", "ready sent", or never emitted a line at all (=> it
|
|
146
|
+
// crashed in a native binding load before this code ran). The pool then
|
|
147
|
+
// attaches whatever stderr it captured to its readiness-failure message,
|
|
148
|
+
// so the operator sees the real cause instead of "did not report ready".
|
|
149
|
+
const BOOTSTRAP_LOG = process.env.CGRAPH_WORKER_BOOTSTRAP === '1' || process.env.CGRAPH_VERBOSE === '1';
|
|
150
|
+
const bootstrapStart = performance.now();
|
|
151
|
+
const bootstrapLog = (stage) => {
|
|
152
|
+
if (!BOOTSTRAP_LOG)
|
|
153
|
+
return;
|
|
154
|
+
const ms = Math.round(performance.now() - bootstrapStart);
|
|
155
|
+
process.stderr.write(`[parse-worker bootstrap] thread=${node_worker_threads_1.threadId} ${stage} (+${ms}ms)\n`);
|
|
156
|
+
};
|
|
157
|
+
// First line we can emit: every static import above (tree-sitter native
|
|
158
|
+
// runtime, v1 language grammars [java/js/ts], vendored grammars, helper
|
|
159
|
+
// modules) has already resolved by the time this module-body statement runs.
|
|
160
|
+
// 非 v1 语法包(python/cpp/csharp/go/rust/php/ruby)按需懒加载(spec §7/§13),
|
|
161
|
+
// 不在此阶段载入。
|
|
162
|
+
bootstrapLog('imports + grammars loaded');
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Worker-local parser + language map
|
|
165
|
+
// ============================================================================
|
|
166
|
+
const parser = new tree_sitter_1.default();
|
|
167
|
+
// v1 语法(java/js/ts/vue[复用 ts])与受保护 vendored 语法(swift/dart/kotlin/c)在
|
|
168
|
+
// 模块加载时已就绪(或为 null),直接入静态表。非 v1 语法(python/cpp/csharp/go/rust/
|
|
169
|
+
// php/ruby)不在此表,由 loadNonV1Grammar 按需解析(spec §7/§13)。
|
|
170
|
+
const languageMap = {
|
|
171
|
+
[index_js_1.SupportedLanguages.JavaScript]: tree_sitter_javascript_1.default,
|
|
172
|
+
[index_js_1.SupportedLanguages.TypeScript]: tree_sitter_typescript_1.default.typescript,
|
|
173
|
+
[`${index_js_1.SupportedLanguages.TypeScript}:tsx`]: tree_sitter_typescript_1.default.tsx,
|
|
174
|
+
[index_js_1.SupportedLanguages.Java]: tree_sitter_java_1.default,
|
|
175
|
+
[index_js_1.SupportedLanguages.Vue]: tree_sitter_typescript_1.default.typescript,
|
|
176
|
+
...(C ? { [index_js_1.SupportedLanguages.C]: C } : {}),
|
|
177
|
+
...(Kotlin ? { [index_js_1.SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
178
|
+
...(Dart ? { [index_js_1.SupportedLanguages.Dart]: Dart } : {}),
|
|
179
|
+
...(Swift ? { [index_js_1.SupportedLanguages.Swift]: Swift } : {}),
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* 解析 worker 语法:先查静态表(v1 + 受保护 vendored),命中即返;否则查非 v1 懒加载
|
|
183
|
+
* 表(首次用到时 require + 缓存)。包未装时返回 null,由调用方降级为结构层收录。
|
|
184
|
+
*/
|
|
185
|
+
const resolveWorkerGrammar = (key) => {
|
|
186
|
+
const staticGrammar = languageMap[key];
|
|
187
|
+
if (staticGrammar != null)
|
|
188
|
+
return staticGrammar;
|
|
189
|
+
return loadNonV1Grammar(key);
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Check if a language grammar is available in this worker.
|
|
193
|
+
* Duplicated from parser-loader.ts because workers can't import from the main thread.
|
|
194
|
+
* Extra filePath parameter needed to distinguish .tsx from .ts (different grammars
|
|
195
|
+
* under the same SupportedLanguages.TypeScript key).
|
|
196
|
+
*/
|
|
197
|
+
const isLanguageAvailable = (language, filePath) => {
|
|
198
|
+
const key = language === index_js_1.SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
199
|
+
? `${language}:tsx`
|
|
200
|
+
: language;
|
|
201
|
+
return resolveWorkerGrammar(key) != null;
|
|
202
|
+
};
|
|
203
|
+
const setLanguage = (language, filePath) => {
|
|
204
|
+
const key = language === index_js_1.SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
205
|
+
? `${language}:tsx`
|
|
206
|
+
: language;
|
|
207
|
+
const lang = resolveWorkerGrammar(key);
|
|
208
|
+
if (!lang)
|
|
209
|
+
throw new Error(`Unsupported language: ${language}`);
|
|
210
|
+
parser.setLanguage(lang);
|
|
211
|
+
};
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Per-file O(1) memoization — avoids repeated parent-chain walks per symbol.
|
|
214
|
+
// Three bare Maps cleared at file boundaries. Map.get() returns undefined for
|
|
215
|
+
// missing keys, so `cached !== undefined` distinguishes "not computed" from
|
|
216
|
+
// a stored null (enclosing class/function not found = top-level).
|
|
217
|
+
// ============================================================================
|
|
218
|
+
const classIdCache = new Map();
|
|
219
|
+
const functionIdCache = new Map();
|
|
220
|
+
const exportCache = new Map();
|
|
221
|
+
const clearCaches = () => {
|
|
222
|
+
classIdCache.clear();
|
|
223
|
+
functionIdCache.clear();
|
|
224
|
+
exportCache.clear();
|
|
225
|
+
fieldInfoCache.clear();
|
|
226
|
+
methodInfoCache.clear();
|
|
227
|
+
};
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// FieldExtractor cache — extract field metadata once per class, reuse for each property.
|
|
230
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
231
|
+
// ============================================================================
|
|
232
|
+
const fieldInfoCache = new Map();
|
|
233
|
+
/**
|
|
234
|
+
* Walk up from a definition node to find the nearest enclosing class/struct/interface
|
|
235
|
+
* AST node. Returns the SyntaxNode itself (not an ID) for passing to FieldExtractor.
|
|
236
|
+
*/
|
|
237
|
+
function findEnclosingClassNode(node) {
|
|
238
|
+
let current = node.parent;
|
|
239
|
+
while (current) {
|
|
240
|
+
if (ast_helpers_js_1.CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
241
|
+
// Return singleton_class directly so the method extractor sees it as
|
|
242
|
+
// the owner node and correctly marks methods as static. Name resolution
|
|
243
|
+
// for qualified names is handled separately by findEnclosingClassInfo.
|
|
244
|
+
return current;
|
|
245
|
+
}
|
|
246
|
+
current = current.parent;
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* For C++ out-of-class method definitions (e.g. `void Foo::bar() {}`), extract the
|
|
252
|
+
* class name from the qualified_identifier scope and find the class declaration in the
|
|
253
|
+
* file's AST. Returns the class SyntaxNode or null if not found.
|
|
254
|
+
*
|
|
255
|
+
* Handles pointer/reference return types where function_declarator is nested inside
|
|
256
|
+
* pointer_declarator or reference_declarator.
|
|
257
|
+
*/
|
|
258
|
+
function findClassNodeByQualifiedName(node) {
|
|
259
|
+
const declarator = node.childForFieldName('declarator');
|
|
260
|
+
if (!declarator)
|
|
261
|
+
return null;
|
|
262
|
+
// Find the function_declarator, recursively unwrapping pointer_declarator /
|
|
263
|
+
// reference_declarator chains (e.g. int** Foo::bar() has
|
|
264
|
+
// pointer_declarator → pointer_declarator → function_declarator).
|
|
265
|
+
let funcDecl = null;
|
|
266
|
+
if (declarator.type === 'function_declarator') {
|
|
267
|
+
funcDecl = declarator;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
let current = declarator;
|
|
271
|
+
while (current && !funcDecl) {
|
|
272
|
+
for (let i = 0; i < current.namedChildCount; i++) {
|
|
273
|
+
const child = current.namedChild(i);
|
|
274
|
+
if (child?.type === 'function_declarator') {
|
|
275
|
+
funcDecl = child;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!funcDecl) {
|
|
280
|
+
const next = current.namedChildren.find((c) => c.type === 'pointer_declarator' || c.type === 'reference_declarator');
|
|
281
|
+
current = next ?? null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (!funcDecl)
|
|
286
|
+
return null;
|
|
287
|
+
// Check if the inner declarator is a qualified_identifier (Foo::bar)
|
|
288
|
+
const innerDecl = funcDecl.childForFieldName('declarator');
|
|
289
|
+
if (!innerDecl || innerDecl.type !== 'qualified_identifier')
|
|
290
|
+
return null;
|
|
291
|
+
const scope = innerDecl.childForFieldName('scope');
|
|
292
|
+
if (!scope)
|
|
293
|
+
return null;
|
|
294
|
+
const className = scope.text;
|
|
295
|
+
// Search the file for a matching class/struct specifier, including inside
|
|
296
|
+
// namespace_definition blocks (the majority of production C++ uses namespaces).
|
|
297
|
+
const root = node.tree.rootNode;
|
|
298
|
+
const classTypes = new Set(['class_specifier', 'struct_specifier']);
|
|
299
|
+
const searchIn = (parent) => {
|
|
300
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
301
|
+
const child = parent.namedChild(i);
|
|
302
|
+
if (!child)
|
|
303
|
+
continue;
|
|
304
|
+
if (classTypes.has(child.type)) {
|
|
305
|
+
const nameNode = child.childForFieldName('name');
|
|
306
|
+
if (nameNode?.text === className)
|
|
307
|
+
return child;
|
|
308
|
+
}
|
|
309
|
+
// Recurse into namespace blocks
|
|
310
|
+
if (child.type === 'namespace_definition') {
|
|
311
|
+
const found = searchIn(child);
|
|
312
|
+
if (found)
|
|
313
|
+
return found;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return null;
|
|
317
|
+
};
|
|
318
|
+
return searchIn(root);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Minimal no-op SymbolTable stub for FieldExtractorContext in the worker.
|
|
322
|
+
* Field extraction only uses symbolTable.lookupExactAll for optional type
|
|
323
|
+
* resolution — returning [] causes the extractor to use the raw type
|
|
324
|
+
* string, which is fine for us. Every other method is a no-op so the
|
|
325
|
+
* stub remains safe if a future FieldExtractor consults it through the
|
|
326
|
+
* full {@link SymbolTableReader} surface.
|
|
327
|
+
*/
|
|
328
|
+
const NOOP_SYMBOL_TABLE = {
|
|
329
|
+
lookupExact: () => undefined,
|
|
330
|
+
lookupExactFull: () => undefined,
|
|
331
|
+
lookupExactAll: () => [],
|
|
332
|
+
lookupCallableByName: () => [],
|
|
333
|
+
getFiles: () => [][Symbol.iterator](),
|
|
334
|
+
getStats: () => ({ fileCount: 0 }),
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Get (or extract and cache) field info for a class node.
|
|
338
|
+
* Returns a name→FieldInfo map, or undefined if the provider has no field extractor
|
|
339
|
+
* or the class yielded no fields.
|
|
340
|
+
*/
|
|
341
|
+
function getFieldInfo(classNode, provider, context) {
|
|
342
|
+
if (!provider.fieldExtractor)
|
|
343
|
+
return undefined;
|
|
344
|
+
const cacheKey = classNode.startIndex;
|
|
345
|
+
let cached = fieldInfoCache.get(cacheKey);
|
|
346
|
+
if (cached)
|
|
347
|
+
return cached;
|
|
348
|
+
const result = provider.fieldExtractor.extract(classNode, context);
|
|
349
|
+
if (!result?.fields?.length)
|
|
350
|
+
return undefined;
|
|
351
|
+
cached = new Map();
|
|
352
|
+
for (const field of result.fields) {
|
|
353
|
+
cached.set(field.name, field);
|
|
354
|
+
}
|
|
355
|
+
fieldInfoCache.set(cacheKey, cached);
|
|
356
|
+
return cached;
|
|
357
|
+
}
|
|
358
|
+
// ============================================================================
|
|
359
|
+
// MethodExtractor cache — extract method metadata once per class, reuse for each method.
|
|
360
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
361
|
+
// ============================================================================
|
|
362
|
+
const methodInfoCache = new Map();
|
|
363
|
+
/**
|
|
364
|
+
* Get (or extract and cache) method info for a class node.
|
|
365
|
+
* Returns a "name:line" → MethodInfo map, or undefined if the provider has no method extractor
|
|
366
|
+
* or the class yielded no methods.
|
|
367
|
+
* Keyed by name:line (not name alone) to support overloaded methods in Java/Kotlin.
|
|
368
|
+
*/
|
|
369
|
+
function getMethodInfo(classNode, provider, context) {
|
|
370
|
+
if (!provider.methodExtractor)
|
|
371
|
+
return undefined;
|
|
372
|
+
const cacheKey = classNode.startIndex;
|
|
373
|
+
let cached = methodInfoCache.get(cacheKey);
|
|
374
|
+
if (cached)
|
|
375
|
+
return cached;
|
|
376
|
+
const result = provider.methodExtractor.extract(classNode, context);
|
|
377
|
+
if (!result?.methods?.length)
|
|
378
|
+
return undefined;
|
|
379
|
+
cached = new Map();
|
|
380
|
+
for (const method of result.methods) {
|
|
381
|
+
cached.set(`${method.name}:${method.line}`, method);
|
|
382
|
+
}
|
|
383
|
+
methodInfoCache.set(cacheKey, cached);
|
|
384
|
+
return cached;
|
|
385
|
+
}
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// Enclosing function detection (for call extraction) — cached
|
|
388
|
+
// ============================================================================
|
|
389
|
+
/**
|
|
390
|
+
* Qualified-name prefix naming the enclosing CALLABLE chain of `node`, or
|
|
391
|
+
* `undefined` when nothing callable encloses it (#2699).
|
|
392
|
+
*
|
|
393
|
+
* Graph node ids are file-scoped, so before this a function-local callable and
|
|
394
|
+
* a file-level one with the same name collapsed onto a single node: a
|
|
395
|
+
* top-level `save()` and `run() { const save = … }` both keyed
|
|
396
|
+
* `Function:<file>:save`, and `run`'s call to its OWN local was attributed to
|
|
397
|
+
* the top-level function — a wrong edge, not a missing one, so `impact` on
|
|
398
|
+
* `save` reported a caller that never calls it. Qualifying the local as
|
|
399
|
+
* `run.save` separates them, mirroring how class members already qualify as
|
|
400
|
+
* `Class.member` (and SCIP's document-scoped `local <id>` keyspace).
|
|
401
|
+
*
|
|
402
|
+
* **This pair is the lockstep guarantee.** The definition phase and the
|
|
403
|
+
* caller-attribution phase (`findEnclosingFunctionId`) each build ids
|
|
404
|
+
* independently, and an id they compute differently is not a test failure —
|
|
405
|
+
* it is a caller silently attaching to a node that does not exist, and the
|
|
406
|
+
* edge vanishing. Both phases therefore derive the nesting prefix from THIS
|
|
407
|
+
* function and nothing else. Keep it that way: any per-phase variation here
|
|
408
|
+
* fails silently.
|
|
409
|
+
*
|
|
410
|
+
* Only a callable that is genuinely nested inside another callable gains a
|
|
411
|
+
* prefix. Top-level functions and ordinary class methods hit the `null` branch
|
|
412
|
+
* and keep their existing ids byte-for-byte, which is what bounds the id churn
|
|
413
|
+
* this change forces.
|
|
414
|
+
*
|
|
415
|
+
* `localIdentity` completes it, and both it and the shared
|
|
416
|
+
* `nestedCallableQualifiedName` rule now live in `./callable-id.ts`: this
|
|
417
|
+
* module posts a `ready` message to `parentPort` at import, so a unit test
|
|
418
|
+
* cannot value-import it, and a rule three phases must agree on has to be
|
|
419
|
+
* testable rather than merely commented (#2714).
|
|
420
|
+
*/
|
|
421
|
+
/**
|
|
422
|
+
* Boundary for the enclosing-callable walk (#2699).
|
|
423
|
+
*
|
|
424
|
+
* `CLASS_CONTAINER_TYPES` lists class DECLARATIONS only. A class can also own
|
|
425
|
+
* members without any declaration node — Java anonymous classes
|
|
426
|
+
* (`object_creation_expression > class_body`), enum-constant bodies, and
|
|
427
|
+
* interface/annotation bodies — and those owners must still stop the walk, or a
|
|
428
|
+
* member of one gets re-keyed as a function-local of the surrounding method.
|
|
429
|
+
*
|
|
430
|
+
* (The dead `NO_QUALIFIED_NAME` sentinel that used to sit below this — which also
|
|
431
|
+
* contained a literal NUL byte — was removed; the cache is two-state: absent =
|
|
432
|
+
* not yet computed, any string = computed.)
|
|
433
|
+
*/
|
|
434
|
+
const CALLABLE_PREFIX_BOUNDARY_TYPES = new Set([
|
|
435
|
+
...ast_helpers_js_1.CLASS_CONTAINER_TYPES,
|
|
436
|
+
// Class bodies (Java, JS/TS, Kotlin) — the owner when the declaration is
|
|
437
|
+
// anonymous or the grammar nests members under a body node.
|
|
438
|
+
'class_body',
|
|
439
|
+
'interface_body',
|
|
440
|
+
'annotation_type_body',
|
|
441
|
+
'enum_body',
|
|
442
|
+
'enum_body_declarations',
|
|
443
|
+
'enum_constant',
|
|
444
|
+
// Anonymous-class construction sites.
|
|
445
|
+
'object_creation_expression', // Java: new Runnable() { ... }
|
|
446
|
+
'object_literal', // Kotlin: object : Runnable { ... }
|
|
447
|
+
'anonymous_object_creation_expression', // C#
|
|
448
|
+
]);
|
|
449
|
+
const enclosingCallablePrefix = (node, filePath, provider) => {
|
|
450
|
+
// Boundary on class-likes: a method's owner is its CLASS, not whatever
|
|
451
|
+
// function that class happens to sit inside. `CLASS_CONTAINER_TYPES` alone is
|
|
452
|
+
// NOT enough for that — it lists only DECLARATION nodes, and an anonymous or
|
|
453
|
+
// body-form class has none. A Java anonymous class is
|
|
454
|
+
// `object_creation_expression > class_body > method_declaration` with no
|
|
455
|
+
// `class_declaration` anywhere, so the walk sailed straight through it to the
|
|
456
|
+
// enclosing method and re-keyed `Worker$1.run` as `Worker.makeHandler.run@7:12`,
|
|
457
|
+
// destroying the javac-compatible JLS identity of #2550/#2555/#2562 (4 existing
|
|
458
|
+
// Java tests). Adding the body/anonymous forms restores the boundary.
|
|
459
|
+
//
|
|
460
|
+
// Over-inclusion here is the SAFE direction: an extra boundary only suppresses
|
|
461
|
+
// the nesting prefix, which falls back to the pre-#2699 class qualification.
|
|
462
|
+
const fnNode = (0, ast_helpers_js_1.findAncestorBeforeBoundary)(node, ast_helpers_js_1.LOCAL_SCOPE_BODY_NODE_TYPES, CALLABLE_PREFIX_BOUNDARY_TYPES) ??
|
|
463
|
+
// Signature/body-split grammars: the enclosing callable is a SIBLING of the
|
|
464
|
+
// body, not an ancestor, so the walk above returns null for every local
|
|
465
|
+
// inside it. Dart is the case in hand (`function_signature` +
|
|
466
|
+
// `function_body` as siblings) — without this a Dart closure gets no
|
|
467
|
+
// prefix, so two same-named closures in one file collapse onto ONE node and
|
|
468
|
+
// the graph asserts a CALLS edge that does not exist in the source (#2699).
|
|
469
|
+
//
|
|
470
|
+
// SPLIT_SIGNATURE_NODE_TYPES, NOT FUNCTION_NODE_TYPES: only a callable that
|
|
471
|
+
// cannot hold its own body can be an enclosing callable of a SIBLING. Using
|
|
472
|
+
// the wider set mis-qualified a file-level PHP `$handler = function …` as
|
|
473
|
+
// `target.$handler` by grabbing the preceding `function target() {…}`.
|
|
474
|
+
(0, ast_helpers_js_1.findSplitBodyCallableAncestor)(node, ast_helpers_js_1.SPLIT_SIGNATURE_NODE_TYPES, CALLABLE_PREFIX_BOUNDARY_TYPES);
|
|
475
|
+
if (fnNode === null)
|
|
476
|
+
return undefined;
|
|
477
|
+
return callableOwnQualifiedName(fnNode, filePath, provider);
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* A callable node's own qualified name, including its enclosing-callable chain.
|
|
481
|
+
* Mutually recursive with `enclosingCallablePrefix`; recursion depth is source
|
|
482
|
+
* nesting depth and every level is memoized, so a file costs O(callables).
|
|
483
|
+
*
|
|
484
|
+
* An ANONYMOUS callable still gets a name — its own source position
|
|
485
|
+
* (`fn@12:9`). ECMAScript creates an environment record for EVERY function
|
|
486
|
+
* whether or not it has a name, so the `save` in
|
|
487
|
+
* `outer() { (function () { const save = … })() }` is a genuinely distinct
|
|
488
|
+
* binding from a file-level `save`. Name-only qualification cannot express
|
|
489
|
+
* that; position can. It is unique by construction (two functions cannot start
|
|
490
|
+
* at the same offset) and deterministic across reparses of the same source.
|
|
491
|
+
* Same reasoning as clang's USR for a function-local (`name@offset`) and
|
|
492
|
+
* Kythe's C++ indexer: a local is not addressable from outside its document,
|
|
493
|
+
* so its identity only has to be unique within it, and source position is the
|
|
494
|
+
* cheapest thing that is. NOT SCIP — SCIP's `local <id>` is a per-document
|
|
495
|
+
* counter and the spec is explicit that locals do not encode the name, so it
|
|
496
|
+
* is prior art for the document-scoped keyspace but not for this key shape.
|
|
497
|
+
*/
|
|
498
|
+
const callableOwnQualifiedName = (fnNode, filePath, provider) => {
|
|
499
|
+
const cached = callableQualifiedNameCache.get(fnNode);
|
|
500
|
+
if (cached !== undefined)
|
|
501
|
+
return cached;
|
|
502
|
+
const efnResult = provider.methodExtractor?.extractFunctionName?.(fnNode, filePath);
|
|
503
|
+
// An anonymous callable has no name of its own, so it IS its position: the
|
|
504
|
+
// `ownName === null` branch below carries the position INSTEAD of a name,
|
|
505
|
+
// never in addition to one, so the two spellings cannot stack.
|
|
506
|
+
const ownName = efnResult?.funcName ?? (0, ast_helpers_js_1.genericFuncName)(fnNode) ?? null;
|
|
507
|
+
const prefix = enclosingCallablePrefix(fnNode, filePath, provider);
|
|
508
|
+
const classInfo = prefix === undefined
|
|
509
|
+
? cachedFindEnclosingClassInfo(fnNode, filePath, provider.resolveEnclosingOwner)
|
|
510
|
+
: null;
|
|
511
|
+
const owner = prefix ?? classInfo?.className;
|
|
512
|
+
const result = prefix !== undefined
|
|
513
|
+
? (0, callable_id_js_1.nestedCallableQualifiedName)(prefix, fnNode, ownName ?? 'fn')
|
|
514
|
+
: ownName === null
|
|
515
|
+
? (0, callable_id_js_1.localIdentity)(fnNode, 'fn')
|
|
516
|
+
: owner
|
|
517
|
+
? `${owner}.${ownName}`
|
|
518
|
+
: ownName;
|
|
519
|
+
callableQualifiedNameCache.set(fnNode, result);
|
|
520
|
+
return result;
|
|
521
|
+
};
|
|
522
|
+
/** Sentinel distinguishing "computed, anonymous" from "not yet computed". */
|
|
523
|
+
const callableQualifiedNameCache = new WeakMap();
|
|
524
|
+
/** Walk up AST to find enclosing function, return its generateId or null for top-level.
|
|
525
|
+
* Applies provider.labelOverride so the label matches the definition phase (single source of truth). */
|
|
526
|
+
const findEnclosingFunctionId = (node, filePath, provider) => {
|
|
527
|
+
const cached = functionIdCache.get(node);
|
|
528
|
+
if (cached !== undefined)
|
|
529
|
+
return cached;
|
|
530
|
+
let current = node.parent;
|
|
531
|
+
while (current) {
|
|
532
|
+
if (ast_helpers_js_1.FUNCTION_NODE_TYPES.has(current.type)) {
|
|
533
|
+
const efnResult = provider.methodExtractor?.extractFunctionName?.(current, filePath);
|
|
534
|
+
const funcName = efnResult?.funcName ?? (0, ast_helpers_js_1.genericFuncName)(current);
|
|
535
|
+
const label = efnResult?.label ?? (0, ast_helpers_js_1.inferFunctionLabel)(current.type);
|
|
536
|
+
if (funcName) {
|
|
537
|
+
// Apply labelOverride so label matches definition phase (e.g., Kotlin Function→Method).
|
|
538
|
+
// null means "skip as definition" — keep original label for scope identification.
|
|
539
|
+
let finalLabel = label;
|
|
540
|
+
if (provider.labelOverride) {
|
|
541
|
+
const override = provider.labelOverride(current, label);
|
|
542
|
+
if (override !== null)
|
|
543
|
+
finalLabel = override;
|
|
544
|
+
}
|
|
545
|
+
// Qualify with enclosing class to match definition-phase node IDs
|
|
546
|
+
const classInfo = cachedFindEnclosingClassInfo(current, filePath, provider.resolveEnclosingOwner);
|
|
547
|
+
const encLang = (0, index_js_3.getLanguageFromFilename)(filePath);
|
|
548
|
+
const standaloneMethodInfo = (finalLabel === 'Method' || finalLabel === 'Constructor') &&
|
|
549
|
+
encLang === index_js_1.SupportedLanguages.Go &&
|
|
550
|
+
provider.methodExtractor?.extractFromNode
|
|
551
|
+
? provider.methodExtractor.extractFromNode(current, {
|
|
552
|
+
filePath,
|
|
553
|
+
language: encLang,
|
|
554
|
+
})
|
|
555
|
+
: null;
|
|
556
|
+
// A nested callable is qualified by its enclosing callable (#2699) and
|
|
557
|
+
// wins over the class/receiver owner: a closure inside a method belongs
|
|
558
|
+
// to the METHOD, not directly to the class, and a Go receiver method can
|
|
559
|
+
// never itself be nested inside another callable.
|
|
560
|
+
const nestedPrefix = enclosingCallablePrefix(current, filePath, provider);
|
|
561
|
+
const ownerName = nestedPrefix ?? classInfo?.className ?? standaloneMethodInfo?.receiverType ?? undefined;
|
|
562
|
+
// Lockstep with the other two id-building phases — see
|
|
563
|
+
// `nestedCallableQualifiedName`, which is the shared rule. When a
|
|
564
|
+
// nested prefix exists it IS `ownerName`, so this branch and the
|
|
565
|
+
// owner branch below cannot disagree about which prefix applies.
|
|
566
|
+
const qualifiedName = nestedPrefix !== undefined
|
|
567
|
+
? (0, callable_id_js_1.nestedCallableQualifiedName)(nestedPrefix, current, funcName)
|
|
568
|
+
: ownerName
|
|
569
|
+
? `${ownerName}.${funcName}`
|
|
570
|
+
: funcName;
|
|
571
|
+
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
|
572
|
+
// Use the same MethodExtractor (getMethodInfo) as the definition phase.
|
|
573
|
+
// When same-arity collisions exist, also append ~type1,type2.
|
|
574
|
+
let arity;
|
|
575
|
+
let encTypeTag = '';
|
|
576
|
+
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
|
577
|
+
if (standaloneMethodInfo) {
|
|
578
|
+
arity = standaloneMethodInfo.parameters.some((p) => p.isVariadic)
|
|
579
|
+
? undefined
|
|
580
|
+
: standaloneMethodInfo.parameters.length;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
const classNode = findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
|
|
584
|
+
if (classNode && encLang) {
|
|
585
|
+
const methodMap = getMethodInfo(classNode, provider, {
|
|
586
|
+
filePath,
|
|
587
|
+
language: encLang,
|
|
588
|
+
});
|
|
589
|
+
const defLine = current.startPosition.row + 1;
|
|
590
|
+
const info = methodMap?.get(`${funcName}:${defLine}`);
|
|
591
|
+
if (info) {
|
|
592
|
+
arity = info.parameters.some((p) => p.isVariadic)
|
|
593
|
+
? undefined
|
|
594
|
+
: info.parameters.length;
|
|
595
|
+
if (methodMap && arity !== undefined) {
|
|
596
|
+
const g = (0, method_props_js_1.buildCollisionGroups)(methodMap);
|
|
597
|
+
encTypeTag =
|
|
598
|
+
(0, method_props_js_1.typeTagForId)(methodMap, funcName, arity, info, encLang, g) +
|
|
599
|
+
(0, method_props_js_1.constTagForId)(methodMap, funcName, arity, info, g);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
const arityTag = arity !== undefined ? `#${arity}${encTypeTag}` : '';
|
|
606
|
+
const result = (0, utils_js_1.generateId)(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
|
|
607
|
+
functionIdCache.set(node, result);
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
// Language-specific enclosing function resolution (e.g., Dart where
|
|
612
|
+
// function_body is a sibling of function_signature, not a child).
|
|
613
|
+
if (provider.enclosingFunctionFinder) {
|
|
614
|
+
const customResult = provider.enclosingFunctionFinder(current);
|
|
615
|
+
if (customResult) {
|
|
616
|
+
let finalLabel = customResult.label;
|
|
617
|
+
if (provider.labelOverride) {
|
|
618
|
+
const override = provider.labelOverride(current.previousSibling, finalLabel);
|
|
619
|
+
if (override !== null)
|
|
620
|
+
finalLabel = override;
|
|
621
|
+
}
|
|
622
|
+
// Qualify custom result with enclosing class
|
|
623
|
+
const classInfo = cachedFindEnclosingClassInfo(current.previousSibling ?? current, filePath, provider.resolveEnclosingOwner);
|
|
624
|
+
// Same nesting rule as the generic branch above (#2699). Anchored on
|
|
625
|
+
// `sigNode`-equivalent (`current.previousSibling ?? current`) so Dart,
|
|
626
|
+
// whose body is a SIBLING of the signature, walks from the same node
|
|
627
|
+
// the class lookup already uses.
|
|
628
|
+
const nestedPrefix2 = enclosingCallablePrefix(current.previousSibling ?? current, filePath, provider);
|
|
629
|
+
const customOwner = nestedPrefix2 ?? classInfo?.className;
|
|
630
|
+
const qualifiedName = customOwner
|
|
631
|
+
? `${customOwner}.${customResult.funcName}`
|
|
632
|
+
: customResult.funcName;
|
|
633
|
+
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
|
634
|
+
// When same-arity collisions exist, also append ~type1,type2.
|
|
635
|
+
const sigNode = current.previousSibling ?? current;
|
|
636
|
+
let arity2;
|
|
637
|
+
let encTypeTag2 = '';
|
|
638
|
+
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
|
639
|
+
const encLang2 = (0, index_js_3.getLanguageFromFilename)(filePath);
|
|
640
|
+
const classNode2 = findEnclosingClassNode(sigNode) ?? findClassNodeByQualifiedName(sigNode);
|
|
641
|
+
if (classNode2 && encLang2) {
|
|
642
|
+
const methodMap2 = getMethodInfo(classNode2, provider, {
|
|
643
|
+
filePath,
|
|
644
|
+
language: encLang2,
|
|
645
|
+
});
|
|
646
|
+
const defLine2 = sigNode.startPosition.row + 1;
|
|
647
|
+
const info2 = methodMap2?.get(`${customResult.funcName}:${defLine2}`);
|
|
648
|
+
if (info2) {
|
|
649
|
+
arity2 = info2.parameters.some((p) => p.isVariadic)
|
|
650
|
+
? undefined
|
|
651
|
+
: info2.parameters.length;
|
|
652
|
+
if (methodMap2 && arity2 !== undefined) {
|
|
653
|
+
const g2 = (0, method_props_js_1.buildCollisionGroups)(methodMap2);
|
|
654
|
+
encTypeTag2 =
|
|
655
|
+
(0, method_props_js_1.typeTagForId)(methodMap2, customResult.funcName, arity2, info2, encLang2, g2) +
|
|
656
|
+
(0, method_props_js_1.constTagForId)(methodMap2, customResult.funcName, arity2, info2, g2);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const arityTag2 = arity2 !== undefined ? `#${arity2}${encTypeTag2}` : '';
|
|
662
|
+
const result = (0, utils_js_1.generateId)(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
|
|
663
|
+
functionIdCache.set(node, result);
|
|
664
|
+
return result;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
current = current.parent;
|
|
668
|
+
}
|
|
669
|
+
functionIdCache.set(node, null);
|
|
670
|
+
return null;
|
|
671
|
+
};
|
|
672
|
+
/** Cached wrapper for findEnclosingClassInfo — avoids repeated parent walks. */
|
|
673
|
+
const cachedFindEnclosingClassInfo = (node, filePath, resolveEnclosingOwner, getQualifiedOwnerName) => {
|
|
674
|
+
const cached = classIdCache.get(node);
|
|
675
|
+
if (cached !== undefined)
|
|
676
|
+
return cached;
|
|
677
|
+
const result = (0, ast_helpers_js_1.findEnclosingClassInfo)(node, filePath, resolveEnclosingOwner, getQualifiedOwnerName);
|
|
678
|
+
classIdCache.set(node, result);
|
|
679
|
+
return result;
|
|
680
|
+
};
|
|
681
|
+
/** Cached wrapper for export checking — avoids repeated parent walks per symbol. */
|
|
682
|
+
const cachedExportCheck = (checker, node, name) => {
|
|
683
|
+
const cached = exportCache.get(node);
|
|
684
|
+
if (cached !== undefined)
|
|
685
|
+
return cached;
|
|
686
|
+
const result = checker(node, name);
|
|
687
|
+
exportCache.set(node, result);
|
|
688
|
+
return result;
|
|
689
|
+
};
|
|
690
|
+
// Label detection moved to shared getLabelFromCaptures in utils.ts
|
|
691
|
+
// DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
|
|
692
|
+
// ============================================================================
|
|
693
|
+
// Process a batch of files
|
|
694
|
+
// ============================================================================
|
|
695
|
+
const processBatch = (files, onProgress) => {
|
|
696
|
+
const result = {
|
|
697
|
+
nodes: [],
|
|
698
|
+
relationships: [],
|
|
699
|
+
symbols: [],
|
|
700
|
+
calls: [],
|
|
701
|
+
assignments: [],
|
|
702
|
+
routes: [],
|
|
703
|
+
fetchCalls: [],
|
|
704
|
+
fetchWrapperDefs: [],
|
|
705
|
+
decoratorRoutes: [],
|
|
706
|
+
routerIncludes: [],
|
|
707
|
+
routerImports: [],
|
|
708
|
+
routerConstructorPrefixes: [],
|
|
709
|
+
routerModuleAliases: [],
|
|
710
|
+
toolDefs: [],
|
|
711
|
+
ormQueries: [],
|
|
712
|
+
constructorBindings: [],
|
|
713
|
+
fileScopeBindings: [],
|
|
714
|
+
parsedFiles: [],
|
|
715
|
+
skippedLanguages: {},
|
|
716
|
+
cfgSkipped: {},
|
|
717
|
+
fileCount: 0,
|
|
718
|
+
};
|
|
719
|
+
// Group by language to minimize setLanguage calls
|
|
720
|
+
const byLanguage = new Map();
|
|
721
|
+
for (const file of files) {
|
|
722
|
+
const lang = (0, index_js_3.getLanguageFromFilename)(file.path);
|
|
723
|
+
if (!lang)
|
|
724
|
+
continue;
|
|
725
|
+
let list = byLanguage.get(lang);
|
|
726
|
+
if (!list) {
|
|
727
|
+
list = [];
|
|
728
|
+
byLanguage.set(lang, list);
|
|
729
|
+
}
|
|
730
|
+
list.push(file);
|
|
731
|
+
}
|
|
732
|
+
let totalProcessed = 0;
|
|
733
|
+
let lastReported = 0;
|
|
734
|
+
const PROGRESS_INTERVAL = Math.max(1, Math.min(100, Math.ceil(files.length / 10)));
|
|
735
|
+
const onFileProcessed = onProgress
|
|
736
|
+
? () => {
|
|
737
|
+
totalProcessed++;
|
|
738
|
+
if (totalProcessed - lastReported >= PROGRESS_INTERVAL) {
|
|
739
|
+
lastReported = totalProcessed;
|
|
740
|
+
onProgress(totalProcessed);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
: undefined;
|
|
744
|
+
for (const [language, langFiles] of byLanguage) {
|
|
745
|
+
const provider = (0, index_js_2.getProvider)(language);
|
|
746
|
+
const queryString = provider.treeSitterQueries;
|
|
747
|
+
if (!queryString) {
|
|
748
|
+
// Standalone providers (regex-based, no tree-sitter) that implement
|
|
749
|
+
// emitScopeCaptures resolve via the scope-resolution pipeline, which
|
|
750
|
+
// re-extracts from source on the main thread.
|
|
751
|
+
if (provider.emitScopeCaptures) {
|
|
752
|
+
// The worker no longer builds `ParsedFile`s for standalone providers
|
|
753
|
+
// either — scope-resolution re-extracts on the main thread, and for
|
|
754
|
+
// standalone COBOL the graph nodes come from cobolPhase, not this
|
|
755
|
+
// artifact (#1983). Count one unit of progress per file, as before.
|
|
756
|
+
for (let i = 0; i < langFiles.length; i++) {
|
|
757
|
+
result.fileCount++;
|
|
758
|
+
onFileProcessed?.();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const tsxFiles = [];
|
|
764
|
+
const regularFiles = [];
|
|
765
|
+
if (language === index_js_1.SupportedLanguages.TypeScript) {
|
|
766
|
+
for (const f of langFiles) {
|
|
767
|
+
if (f.path.endsWith('.tsx')) {
|
|
768
|
+
tsxFiles.push(f);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
regularFiles.push(f);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
// Manual loop (not spread) — `push(...arr)` blows the stack on very
|
|
777
|
+
// large arrays when langFiles has tens of thousands of entries.
|
|
778
|
+
for (const f of langFiles)
|
|
779
|
+
regularFiles.push(f);
|
|
780
|
+
}
|
|
781
|
+
// Process regular files for this language
|
|
782
|
+
if (regularFiles.length > 0) {
|
|
783
|
+
if (isLanguageAvailable(language, regularFiles[0].path)) {
|
|
784
|
+
try {
|
|
785
|
+
setLanguage(language, regularFiles[0].path);
|
|
786
|
+
processFileGroup(regularFiles, language, queryString, result, onFileProcessed);
|
|
787
|
+
}
|
|
788
|
+
catch (err) {
|
|
789
|
+
// A throw here drops the whole language group — surface it to the pool
|
|
790
|
+
// (#2264) instead of silently skipping. The old empty catch hid real
|
|
791
|
+
// extractor/parser failures, not just an unavailable grammar.
|
|
792
|
+
reportWarning(`Skipped ${regularFiles.length} ${language} file(s) after a processing error: ${err instanceof Error ? err.message : String(err)}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
result.skippedLanguages[language] =
|
|
797
|
+
(result.skippedLanguages[language] || 0) + regularFiles.length;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
// Process tsx files separately (different grammar)
|
|
801
|
+
if (tsxFiles.length > 0) {
|
|
802
|
+
if (isLanguageAvailable(language, tsxFiles[0].path)) {
|
|
803
|
+
try {
|
|
804
|
+
setLanguage(language, tsxFiles[0].path);
|
|
805
|
+
processFileGroup(tsxFiles, language, queryString, result, onFileProcessed);
|
|
806
|
+
}
|
|
807
|
+
catch (err) {
|
|
808
|
+
// See above — surface a tsx-group processing failure rather than
|
|
809
|
+
// silently dropping every file in it (#2264).
|
|
810
|
+
reportWarning(`Skipped ${tsxFiles.length} ${language} (tsx) file(s) after a processing error: ${err instanceof Error ? err.message : String(err)}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
result.skippedLanguages[language] =
|
|
815
|
+
(result.skippedLanguages[language] || 0) + tsxFiles.length;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (onProgress && totalProcessed !== lastReported) {
|
|
820
|
+
onProgress(totalProcessed);
|
|
821
|
+
}
|
|
822
|
+
return result;
|
|
823
|
+
};
|
|
824
|
+
// Express/Hono method names that register routes
|
|
825
|
+
const EXPRESS_ROUTE_METHODS = new Set([
|
|
826
|
+
'get',
|
|
827
|
+
'post',
|
|
828
|
+
'put',
|
|
829
|
+
'delete',
|
|
830
|
+
'patch',
|
|
831
|
+
'all',
|
|
832
|
+
'use',
|
|
833
|
+
'route',
|
|
834
|
+
]);
|
|
835
|
+
/**
|
|
836
|
+
* Walk a tree-sitter AST subtree looking for a call to the global `fetch()` function.
|
|
837
|
+
* Returns `true` if found within `maxDepth` levels of nesting — keeps the check
|
|
838
|
+
* lightweight so it doesn't slow down parse-worker on large function bodies.
|
|
839
|
+
*/
|
|
840
|
+
const checkForFetchCall = (node, depth = 0, maxDepth = 5) => {
|
|
841
|
+
if (depth > maxDepth)
|
|
842
|
+
return false;
|
|
843
|
+
if (node.type === 'call_expression') {
|
|
844
|
+
const fn = node.childForFieldName('function');
|
|
845
|
+
if (fn?.type === 'identifier' && fn.text === 'fetch')
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
849
|
+
if (checkForFetchCall(node.child(i), depth + 1, maxDepth))
|
|
850
|
+
return true;
|
|
851
|
+
}
|
|
852
|
+
return false;
|
|
853
|
+
};
|
|
854
|
+
// HTTP client methods that are ONLY used by clients, not Express route registration.
|
|
855
|
+
// Methods like get/post/put/delete/patch overlap with Express — those are captured by
|
|
856
|
+
// the express_route handler as route definitions, not consumers. The fetch() global
|
|
857
|
+
// function is captured separately by the route.fetch query.
|
|
858
|
+
const HTTP_CLIENT_ONLY_METHODS = new Set(['head', 'options', 'request', 'ajax']);
|
|
859
|
+
// Known HTTP client receivers u2014 skip these, they're API consumers not routes
|
|
860
|
+
const HTTP_CLIENT_RECEIVERS = new Set([
|
|
861
|
+
'axios',
|
|
862
|
+
'request',
|
|
863
|
+
'fetch',
|
|
864
|
+
'http',
|
|
865
|
+
'https',
|
|
866
|
+
'got',
|
|
867
|
+
'ky',
|
|
868
|
+
'superagent',
|
|
869
|
+
'needle',
|
|
870
|
+
'undici',
|
|
871
|
+
'apiclient',
|
|
872
|
+
'client',
|
|
873
|
+
'httpclient',
|
|
874
|
+
'api',
|
|
875
|
+
'$http',
|
|
876
|
+
'session',
|
|
877
|
+
'httpservice',
|
|
878
|
+
'conn',
|
|
879
|
+
]);
|
|
880
|
+
// Decorator names that indicate HTTP route handlers (NestJS, Flask, FastAPI, Spring)
|
|
881
|
+
const ROUTE_DECORATOR_NAMES = new Set([
|
|
882
|
+
'Get',
|
|
883
|
+
'Post',
|
|
884
|
+
'Put',
|
|
885
|
+
'Delete',
|
|
886
|
+
'Patch',
|
|
887
|
+
'Route',
|
|
888
|
+
'get',
|
|
889
|
+
'post',
|
|
890
|
+
'put',
|
|
891
|
+
'delete',
|
|
892
|
+
'patch',
|
|
893
|
+
'route',
|
|
894
|
+
'RequestMapping',
|
|
895
|
+
'GetMapping',
|
|
896
|
+
'PostMapping',
|
|
897
|
+
'PutMapping',
|
|
898
|
+
'DeleteMapping',
|
|
899
|
+
'PatchMapping',
|
|
900
|
+
]);
|
|
901
|
+
// ============================================================================
|
|
902
|
+
// ORM Query Detection (Prisma + Supabase)
|
|
903
|
+
// ============================================================================
|
|
904
|
+
const PRISMA_QUERY_RE = /\bprisma\.(\w+)\.(findMany|findFirst|findUnique|findUniqueOrThrow|findFirstOrThrow|create|createMany|update|updateMany|delete|deleteMany|upsert|count|aggregate|groupBy)\s*\(/g;
|
|
905
|
+
const SUPABASE_QUERY_RE = /\bsupabase\.from\s*\(\s*['"](\w+)['"]\s*\)\s*\.(select|insert|update|delete|upsert)\s*\(/g;
|
|
906
|
+
/**
|
|
907
|
+
* Extract ORM query calls from file content via regex.
|
|
908
|
+
* Appends results to the provided array (avoids allocation when no matches).
|
|
909
|
+
*/
|
|
910
|
+
function extractORMQueries(filePath, content, out) {
|
|
911
|
+
const hasPrisma = content.includes('prisma.');
|
|
912
|
+
const hasSupabase = content.includes('supabase.from');
|
|
913
|
+
if (!hasPrisma && !hasSupabase)
|
|
914
|
+
return;
|
|
915
|
+
if (hasPrisma) {
|
|
916
|
+
PRISMA_QUERY_RE.lastIndex = 0;
|
|
917
|
+
let m;
|
|
918
|
+
while ((m = PRISMA_QUERY_RE.exec(content)) !== null) {
|
|
919
|
+
const model = m[1];
|
|
920
|
+
if (model.startsWith('$'))
|
|
921
|
+
continue;
|
|
922
|
+
out.push({
|
|
923
|
+
filePath,
|
|
924
|
+
orm: 'prisma',
|
|
925
|
+
model,
|
|
926
|
+
method: m[2],
|
|
927
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (hasSupabase) {
|
|
932
|
+
SUPABASE_QUERY_RE.lastIndex = 0;
|
|
933
|
+
let m;
|
|
934
|
+
while ((m = SUPABASE_QUERY_RE.exec(content)) !== null) {
|
|
935
|
+
out.push({
|
|
936
|
+
filePath,
|
|
937
|
+
orm: 'supabase',
|
|
938
|
+
model: m[1],
|
|
939
|
+
method: m[2],
|
|
940
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// ============================================================================
|
|
946
|
+
// FastAPI router prefix detection (Python)
|
|
947
|
+
// ============================================================================
|
|
948
|
+
//
|
|
949
|
+
// The extraction lives in `../route-extractors/fastapi-router-bindings`
|
|
950
|
+
// (a pure-function module — NOT a worker, no `worker_threads`, no
|
|
951
|
+
// `parentPort`). It's imported here only so the worker entry can call it
|
|
952
|
+
// per file; this module does not re-export it. Downstream consumers
|
|
953
|
+
// import the function and its types directly from `route-extractors/`.
|
|
954
|
+
const fastapi_router_bindings_js_1 = require("../route-extractors/fastapi-router-bindings.js");
|
|
955
|
+
const python_const_resolver_js_1 = require("../route-extractors/python-const-resolver.js");
|
|
956
|
+
/**
|
|
957
|
+
* Report a non-fatal worker issue to the pool over IPC so a caught error is not
|
|
958
|
+
* invisible to the operator (#2264). The pool logs it on the main thread AND
|
|
959
|
+
* resets the worker idle timer (so a worker grinding through failing files isn't
|
|
960
|
+
* falsely idle-evicted). Falls back to the local logger when there's no parent —
|
|
961
|
+
* this code also runs on the main thread in tests / the non-worker path. Fatal,
|
|
962
|
+
* group-aborting errors go through the message handler's
|
|
963
|
+
* `{ type: 'error', errorStack }` channel instead.
|
|
964
|
+
*/
|
|
965
|
+
function reportWarning(message) {
|
|
966
|
+
if (node_worker_threads_1.parentPort) {
|
|
967
|
+
node_worker_threads_1.parentPort.postMessage({ type: 'warning', message });
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
logger_js_1.logger.warn(message);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
const processFileGroup = (files, language, queryString, result, onFileProcessed) => {
|
|
974
|
+
let query;
|
|
975
|
+
try {
|
|
976
|
+
const lang = parser.getLanguage();
|
|
977
|
+
query = new tree_sitter_1.default.Query(lang, queryString);
|
|
978
|
+
}
|
|
979
|
+
catch (err) {
|
|
980
|
+
reportWarning(`Query compilation failed for ${language}: ${err instanceof Error ? err.message : String(err)}`);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
for (const file of files) {
|
|
984
|
+
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
985
|
+
if ((0, constants_js_1.getTreeSitterContentByteLength)(file.content) > constants_js_1.TREE_SITTER_MAX_BUFFER)
|
|
986
|
+
continue;
|
|
987
|
+
// Authoritative in-flight signal for the pool: lets `WorkerPool` exclude
|
|
988
|
+
// exactly this file if the worker dies during parse/extract, instead of
|
|
989
|
+
// guessing from `items[lastProgress]` (which the language-grouped order
|
|
990
|
+
// here would defeat). The pool gracefully ignores this when running an
|
|
991
|
+
// older worker build that doesn't emit it.
|
|
992
|
+
if (node_worker_threads_1.parentPort) {
|
|
993
|
+
node_worker_threads_1.parentPort.postMessage({ type: 'starting-file', path: file.path });
|
|
994
|
+
}
|
|
995
|
+
// Vue SFC preprocessing: extract <script> block content
|
|
996
|
+
let parseContent = file.content;
|
|
997
|
+
let scopeSourceKind = 'full-file';
|
|
998
|
+
let lineOffset = 0;
|
|
999
|
+
let isVueSetup = false;
|
|
1000
|
+
if (language === index_js_1.SupportedLanguages.Vue) {
|
|
1001
|
+
const extracted = (0, vue_sfc_extractor_js_1.extractVueScript)(file.content);
|
|
1002
|
+
if (!extracted)
|
|
1003
|
+
continue; // skip .vue files with no script block
|
|
1004
|
+
parseContent = extracted.scriptContent;
|
|
1005
|
+
scopeSourceKind = 'pre-extracted-script';
|
|
1006
|
+
lineOffset = extracted.lineOffset;
|
|
1007
|
+
isVueSetup = extracted.isSetup;
|
|
1008
|
+
}
|
|
1009
|
+
// Per-language source-text transform (e.g., UE macro stripping for C++).
|
|
1010
|
+
// Length-preserving — see LanguageProvider.preprocessSource contract.
|
|
1011
|
+
parseContent =
|
|
1012
|
+
(0, index_js_2.getProvider)(language).preprocessSource?.(parseContent, file.path) ?? parseContent;
|
|
1013
|
+
clearCaches(); // Reset memoization before each new file
|
|
1014
|
+
let tree;
|
|
1015
|
+
try {
|
|
1016
|
+
tree = (0, safe_parse_js_1.parseSourceSafe)(parser, parseContent, undefined, {
|
|
1017
|
+
bufferSize: (0, constants_js_1.getTreeSitterBufferSize)(parseContent),
|
|
1018
|
+
}, file.path);
|
|
1019
|
+
}
|
|
1020
|
+
catch (err) {
|
|
1021
|
+
reportWarning(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
result.fileCount++;
|
|
1025
|
+
onFileProcessed?.();
|
|
1026
|
+
let matches;
|
|
1027
|
+
try {
|
|
1028
|
+
matches = query.matches(tree.rootNode);
|
|
1029
|
+
}
|
|
1030
|
+
catch (err) {
|
|
1031
|
+
reportWarning(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
const provider = (0, index_js_2.getProvider)(language);
|
|
1035
|
+
// #2687: ONE pass over `matches` yields both suppression sets — the
|
|
1036
|
+
// definition-name claims by rank (callable > Property > value), so the dedup
|
|
1037
|
+
// below cannot depend on tree-sitter's match order, and the concrete-typedef
|
|
1038
|
+
// ranges the typedef guard consumes.
|
|
1039
|
+
const definitionPreScan = (0, ast_helpers_js_1.buildDefinitionPreScan)(matches, provider);
|
|
1040
|
+
const concreteTypedefRanges = definitionPreScan.concreteTypedefRanges;
|
|
1041
|
+
// Produce the `ParsedFile` for the scope-resolution pipeline HERE, reusing
|
|
1042
|
+
// the tree we just parsed (no second tree-sitter parse). Scope-resolution
|
|
1043
|
+
// consumes these via the disk-backed parsedfile-store instead of
|
|
1044
|
+
// re-extracting each file from source on the main thread — which
|
|
1045
|
+
// accumulated an unbounded native tree-sitter leak on huge repos (#1983;
|
|
1046
|
+
// see parsedfile-store.ts). parse-impl flushes `result.parsedFiles` to disk
|
|
1047
|
+
// per chunk and does NOT retain them in main-thread heap, so this no longer
|
|
1048
|
+
// costs ~1× the semantic model in RAM during parse.
|
|
1049
|
+
const parsedFile = (0, scope_extractor_bridge_js_1.extractParsedFile)(provider, parseContent, file.path, reportWarning, tree, scopeSourceKind);
|
|
1050
|
+
if (parsedFile !== undefined) {
|
|
1051
|
+
// Capture-time side-channel (#1983): `extractParsedFile` just ran the
|
|
1052
|
+
// provider's `emitScopeCaptures`, which (for C++ ADL/namespace marks,
|
|
1053
|
+
// C `static`-linkage names, and Kotlin companion scopes) populated
|
|
1054
|
+
// module-level maps as a SIDE EFFECT that is NOT on `parsedFile`'s
|
|
1055
|
+
// scopes/defs. Snapshot
|
|
1056
|
+
// that per-file state as plain data onto `ParsedFile.captureSideChannel`
|
|
1057
|
+
// so the main thread can restore it (via `ScopeResolver.applyCaptureSideChannel`)
|
|
1058
|
+
// WITHOUT a re-parse, after this ParsedFile crosses the worker boundary /
|
|
1059
|
+
// disk store. Providers without capture-time side effects leave the hook
|
|
1060
|
+
// undefined and this is a no-op. `undefined` return ⇒ no field added.
|
|
1061
|
+
//
|
|
1062
|
+
// `extractParsedFile` returns a frozen ParsedFile, so re-wrap (shallow
|
|
1063
|
+
// copy — scopes/defs are carried by reference) to attach the field rather
|
|
1064
|
+
// than mutate the frozen object.
|
|
1065
|
+
const sideChannel = provider.collectCaptureSideChannel?.(file.path);
|
|
1066
|
+
let withChannels = sideChannel !== undefined ? { ...parsedFile, captureSideChannel: sideChannel } : parsedFile;
|
|
1067
|
+
// CFG side-channel (#2081 M1): build the per-function control-flow graph
|
|
1068
|
+
// here, where the tree-sitter AST is still in hand, and attach it as plain
|
|
1069
|
+
// serializable data. Only on a --pdg run and only for languages with a
|
|
1070
|
+
// cfgVisitor (TS/JS in M1). The same disk-store/warm-cache machinery that
|
|
1071
|
+
// carries captureSideChannel carries this — its coherence rests on the
|
|
1072
|
+
// SCHEMA_BUMP + the pdg-folded chunk-hash key (see parse-cache.ts).
|
|
1073
|
+
if (PDG_ENABLED && provider.cfgVisitor) {
|
|
1074
|
+
// Isolate the CFG build per file: a throw here (an unexpected tree-sitter
|
|
1075
|
+
// node shape) must NOT propagate — it would escape processFileGroup to the
|
|
1076
|
+
// language-group catch, which treats any throw as "parser unavailable" and
|
|
1077
|
+
// silently drops EVERY remaining file in the group. Skip CFG for this one
|
|
1078
|
+
// file; parsing + scope resolution proceed unaffected (CFG is a
|
|
1079
|
+
// strictly-additive opt-in). collectFunctionCfgs ALSO isolates per
|
|
1080
|
+
// FUNCTION now (#2195) — a deep-nesting bail or a single malformed function
|
|
1081
|
+
// is counted in `skipped` and skipped, not allowed to lose the whole file.
|
|
1082
|
+
try {
|
|
1083
|
+
const { cfgs, skipped } = (0, collect_js_1.collectFunctionCfgs)(tree.rootNode, provider.cfgVisitor, file.path, PDG_MAX_FUNCTION_LINES,
|
|
1084
|
+
// Embedded scripts (Vue SFC <script>) parse at row 0 but live at
|
|
1085
|
+
// `lineOffset` in the file — shift the CFG into file coordinates so
|
|
1086
|
+
// it joins its graph node and BasicBlock lines map to source.
|
|
1087
|
+
lineOffset);
|
|
1088
|
+
if (cfgs.length)
|
|
1089
|
+
withChannels = { ...withChannels, cfgSideChannel: cfgs };
|
|
1090
|
+
// Surface per-function CFG skips per-language (#2195): merged + logged
|
|
1091
|
+
// in mergeChunkResults. Only accumulate when something was skipped so
|
|
1092
|
+
// the common (nothing-skipped) case stays a no-op.
|
|
1093
|
+
if (skipped.tooManyLines || skipped.tooDeeplyNested || skipped.buildError) {
|
|
1094
|
+
const agg = (result.cfgSkipped ??= {});
|
|
1095
|
+
const prev = agg[language] ?? { tooManyLines: 0, tooDeeplyNested: 0, buildError: 0 };
|
|
1096
|
+
agg[language] = {
|
|
1097
|
+
tooManyLines: prev.tooManyLines + skipped.tooManyLines,
|
|
1098
|
+
tooDeeplyNested: prev.tooDeeplyNested + skipped.tooDeeplyNested,
|
|
1099
|
+
buildError: prev.buildError + skipped.buildError,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
catch (err) {
|
|
1104
|
+
reportWarning(`CFG build failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
result.parsedFiles.push(withChannels);
|
|
1108
|
+
}
|
|
1109
|
+
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
1110
|
+
// The legacy heritage pre-pass that seeded a file-local parentMap for
|
|
1111
|
+
// buildTypeEnv was removed in RING4-1 (#942) along with the rest of the
|
|
1112
|
+
// call-resolution DAG. Inheritance is now emitted by scope-resolution
|
|
1113
|
+
// (preEmitInheritanceEdges + @reference.inherits), so buildTypeEnv runs with
|
|
1114
|
+
// an empty parentMap — cross-file inheritance was never resolved here anyway.
|
|
1115
|
+
const parentMap = new Map();
|
|
1116
|
+
const typeEnv = (0, type_env_js_1.buildTypeEnv)(tree, language, {
|
|
1117
|
+
filePath: file.path,
|
|
1118
|
+
parentMap,
|
|
1119
|
+
enclosingFunctionFinder: provider?.enclosingFunctionFinder,
|
|
1120
|
+
extractFunctionName: provider?.methodExtractor?.extractFunctionName,
|
|
1121
|
+
});
|
|
1122
|
+
const callRouter = provider.callRouter;
|
|
1123
|
+
if (typeEnv.constructorBindings.length > 0) {
|
|
1124
|
+
result.constructorBindings.push({
|
|
1125
|
+
filePath: file.path,
|
|
1126
|
+
bindings: [...typeEnv.constructorBindings],
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
// Serialize file-scope bindings for BindingAccumulator. These feed the
|
|
1130
|
+
// ExportedTypeMap enrichment loop in pipeline.ts — the only current
|
|
1131
|
+
// consumer of worker-path binding data.
|
|
1132
|
+
//
|
|
1133
|
+
// Historical note: we previously serialized all scopes
|
|
1134
|
+
// (`typeEnv.allScopes()`), which pushed ~4.9 MB of function-scope
|
|
1135
|
+
// bindings across the IPC boundary on every worker batch with zero
|
|
1136
|
+
// downstream readers. Narrowing to `fileScope()` recovers that cost.
|
|
1137
|
+
// See the `FileScopeBindings` JSDoc above for the Phase 9 reversion
|
|
1138
|
+
// path when a function-scope consumer lands.
|
|
1139
|
+
const fileScope = typeEnv.fileScope();
|
|
1140
|
+
if (fileScope.size > 0) {
|
|
1141
|
+
const scopeBindings = [];
|
|
1142
|
+
for (const [varName, typeName] of fileScope) {
|
|
1143
|
+
scopeBindings.push([varName, typeName]);
|
|
1144
|
+
}
|
|
1145
|
+
result.fileScopeBindings.push({ filePath: file.path, bindings: scopeBindings });
|
|
1146
|
+
}
|
|
1147
|
+
// Per-file map: decorator end-line → decorator info, for associating with definitions
|
|
1148
|
+
const fileDecorators = new Map();
|
|
1149
|
+
// Track start indices of definition nodes already processed by higher-priority captures
|
|
1150
|
+
// (e.g. @definition.function) to avoid duplicate nodes when @definition.const/@definition.variable
|
|
1151
|
+
// patterns overlap with the same source range.
|
|
1152
|
+
const processedDefinitionNodes = new Set();
|
|
1153
|
+
const variableInfoCache = new Map();
|
|
1154
|
+
for (const match of matches) {
|
|
1155
|
+
const captureMap = {};
|
|
1156
|
+
for (const c of match.captures) {
|
|
1157
|
+
captureMap[c.name] = c.node;
|
|
1158
|
+
}
|
|
1159
|
+
if ((0, ast_helpers_js_1.isSuppressedConcreteTypedefDuplicate)(captureMap, concreteTypedefRanges))
|
|
1160
|
+
continue;
|
|
1161
|
+
// Import matches: IMPORTS edges are emitted by the scope-resolution
|
|
1162
|
+
// phase from finalized ImportEdges (RING4-1 #942 / RING4-2 #943 removed
|
|
1163
|
+
// the legacy per-file import-map extraction that ran here). Skip.
|
|
1164
|
+
if (captureMap['import'] && captureMap['import.source']) {
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
// Extract assignment sites (field write access)
|
|
1168
|
+
if (captureMap['assignment'] &&
|
|
1169
|
+
captureMap['assignment.receiver'] &&
|
|
1170
|
+
captureMap['assignment.property']) {
|
|
1171
|
+
const receiverText = captureMap['assignment.receiver'].text;
|
|
1172
|
+
const propertyName = captureMap['assignment.property'].text;
|
|
1173
|
+
if (receiverText && propertyName) {
|
|
1174
|
+
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path, provider) ||
|
|
1175
|
+
(0, utils_js_1.generateId)('File', file.path);
|
|
1176
|
+
let receiverTypeName;
|
|
1177
|
+
if (typeEnv) {
|
|
1178
|
+
receiverTypeName = typeEnv.lookup(receiverText, captureMap['assignment']) ?? undefined;
|
|
1179
|
+
}
|
|
1180
|
+
result.assignments.push({
|
|
1181
|
+
filePath: file.path,
|
|
1182
|
+
sourceId: srcId,
|
|
1183
|
+
receiverText,
|
|
1184
|
+
propertyName,
|
|
1185
|
+
line: captureMap['assignment'].startPosition.row + 1,
|
|
1186
|
+
...(receiverTypeName ? { receiverTypeName } : {}),
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
if (!captureMap['call'])
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
// Store decorator metadata for later association with definitions
|
|
1193
|
+
if (captureMap['decorator'] && captureMap['decorator.name']) {
|
|
1194
|
+
const decoratorName = captureMap['decorator.name'].text;
|
|
1195
|
+
const decoratorArg = captureMap['decorator.arg']?.text;
|
|
1196
|
+
// #2391: the first positional arg captured as either a string node
|
|
1197
|
+
// (`arg_str`, present even for the empty-string literal `""` which has no
|
|
1198
|
+
// `string_content`) or a non-literal expression (`arg_expr`: an
|
|
1199
|
+
// identifier or a `+`-concatenation).
|
|
1200
|
+
const decoratorArgStr = captureMap['decorator.arg_str'];
|
|
1201
|
+
const decoratorArgExpr = captureMap['decorator.arg_expr'];
|
|
1202
|
+
const decoratorReceiver = captureMap['decorator.receiver']?.text;
|
|
1203
|
+
const decoratorNode = captureMap['decorator'];
|
|
1204
|
+
// Store by the decorator's end line — the definition follows immediately after
|
|
1205
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
1206
|
+
name: decoratorName,
|
|
1207
|
+
arg: decoratorArg,
|
|
1208
|
+
});
|
|
1209
|
+
if (ROUTE_DECORATOR_NAMES.has(decoratorName)) {
|
|
1210
|
+
const method = decoratorName.replace('Mapping', '').toUpperCase();
|
|
1211
|
+
const httpMethod = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method)
|
|
1212
|
+
? method
|
|
1213
|
+
: 'GET';
|
|
1214
|
+
const base = {
|
|
1215
|
+
filePath: file.path,
|
|
1216
|
+
httpMethod,
|
|
1217
|
+
decoratorName,
|
|
1218
|
+
lineNumber: decoratorNode.startPosition.row + lineOffset,
|
|
1219
|
+
...(decoratorReceiver ? { decoratorReceiver } : {}),
|
|
1220
|
+
};
|
|
1221
|
+
if (decoratorArgStr) {
|
|
1222
|
+
// String-literal path (the fast path, unchanged). Empty-string
|
|
1223
|
+
// literal `""` has no `string_content` → `decoratorArg` undefined →
|
|
1224
|
+
// routePath '' (a valid path under an APIRouter prefix).
|
|
1225
|
+
result.decoratorRoutes.push({ ...base, routePath: decoratorArg ?? '' });
|
|
1226
|
+
}
|
|
1227
|
+
else if (decoratorArgExpr) {
|
|
1228
|
+
// #2391 non-literal path (imported/composed constant). Emit the raw
|
|
1229
|
+
// expression + its operands for cross-file resolution in parse-impl;
|
|
1230
|
+
// `routePath` stays empty until resolved (or the route is dropped).
|
|
1231
|
+
const operands = decoratorArgExpr.type === 'identifier'
|
|
1232
|
+
? [{ kind: 'ref', name: decoratorArgExpr.text }]
|
|
1233
|
+
: (0, python_const_resolver_js_1.parseConstOperands)(decoratorArgExpr);
|
|
1234
|
+
result.decoratorRoutes.push({
|
|
1235
|
+
...base,
|
|
1236
|
+
routePath: '',
|
|
1237
|
+
routePathExpr: decoratorArgExpr.text,
|
|
1238
|
+
...(operands ? { routePathOperands: operands } : {}),
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
// Otherwise the first arg is absent or an unsupported shape
|
|
1242
|
+
// (attribute access, call, …) → skip; never a phantom `POST /`.
|
|
1243
|
+
}
|
|
1244
|
+
// MCP/RPC tool detection: @mcp.tool(), @app.tool(), @server.tool()
|
|
1245
|
+
if (decoratorName === 'tool') {
|
|
1246
|
+
// Re-store with isTool flag for the definition handler
|
|
1247
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
1248
|
+
name: decoratorName,
|
|
1249
|
+
arg: decoratorArg,
|
|
1250
|
+
isTool: true,
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
// Extract HTTP consumer URLs: fetch(), axios.get(), $.get(), requests.get(), etc.
|
|
1256
|
+
if (captureMap['route.fetch']) {
|
|
1257
|
+
const urlNode = captureMap['route.url'] ?? captureMap['route.template_url'];
|
|
1258
|
+
if (urlNode) {
|
|
1259
|
+
result.fetchCalls.push({
|
|
1260
|
+
filePath: file.path,
|
|
1261
|
+
fetchURL: urlNode.text,
|
|
1262
|
+
lineNumber: captureMap['route.fetch'].startPosition.row + lineOffset,
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
// HTTP client calls: axios.get('/path'), $.post('/path'), requests.get('/path')
|
|
1268
|
+
// Skip methods also in EXPRESS_ROUTE_METHODS to avoid double-registering Express
|
|
1269
|
+
// routes as both route definitions AND consumers (both queries match same AST node)
|
|
1270
|
+
if (captureMap['http_client'] && captureMap['http_client.url']) {
|
|
1271
|
+
const method = captureMap['http_client.method']?.text;
|
|
1272
|
+
const url = captureMap['http_client.url'].text;
|
|
1273
|
+
if (method && HTTP_CLIENT_ONLY_METHODS.has(method) && url.startsWith('/')) {
|
|
1274
|
+
result.fetchCalls.push({
|
|
1275
|
+
filePath: file.path,
|
|
1276
|
+
fetchURL: url,
|
|
1277
|
+
lineNumber: captureMap['http_client'].startPosition.row + lineOffset,
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
// Express/Hono route registration: app.get('/path', handler)
|
|
1283
|
+
if (captureMap['express_route'] &&
|
|
1284
|
+
captureMap['express_route.method'] &&
|
|
1285
|
+
captureMap['express_route.path']) {
|
|
1286
|
+
const method = captureMap['express_route.method'].text;
|
|
1287
|
+
const routePath = captureMap['express_route.path'].text;
|
|
1288
|
+
if (EXPRESS_ROUTE_METHODS.has(method) && routePath.startsWith('/')) {
|
|
1289
|
+
// Extract the receiver (the object the method is called on) to filter out
|
|
1290
|
+
// HTTP client calls like axios.get('/api/users') that match the same pattern
|
|
1291
|
+
// as Express route registrations.
|
|
1292
|
+
const callNode = captureMap['express_route'];
|
|
1293
|
+
const funcNode = callNode.childForFieldName?.('function') ?? callNode.children?.[0];
|
|
1294
|
+
// Walk through nested member_expressions and call_expressions to
|
|
1295
|
+
// reach the innermost receiver identifier. Handles chains like:
|
|
1296
|
+
// this.httpService.get('/path') -> member chain -> 'httpservice'
|
|
1297
|
+
// getClient().get('/path') -> call_expression -> 'getclient'
|
|
1298
|
+
// axios.get('/path') -> bare identifier -> 'axios'
|
|
1299
|
+
let receiverNode = funcNode?.childForFieldName?.('object') ?? funcNode?.children?.[0];
|
|
1300
|
+
while (receiverNode?.type === 'member_expression' ||
|
|
1301
|
+
receiverNode?.type === 'call_expression') {
|
|
1302
|
+
if (receiverNode.type === 'member_expression') {
|
|
1303
|
+
// Drill into the property (rightmost part) of the member expression
|
|
1304
|
+
const propNode = receiverNode.childForFieldName?.('property');
|
|
1305
|
+
if (propNode) {
|
|
1306
|
+
receiverNode = propNode;
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
// call_expression: unwrap to the function being called
|
|
1314
|
+
const innerFunc = receiverNode.childForFieldName?.('function') ?? receiverNode.children?.[0];
|
|
1315
|
+
if (innerFunc && innerFunc !== receiverNode) {
|
|
1316
|
+
receiverNode = innerFunc;
|
|
1317
|
+
}
|
|
1318
|
+
else {
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
const receiverText = receiverNode?.text?.toLowerCase() ?? '';
|
|
1324
|
+
if (HTTP_CLIENT_RECEIVERS.has(receiverText)) {
|
|
1325
|
+
// This is an HTTP client call, not a route definition u2014 skip it
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const httpMethod = method === 'all' || method === 'use' || method === 'route'
|
|
1329
|
+
? 'GET'
|
|
1330
|
+
: method.toUpperCase();
|
|
1331
|
+
result.decoratorRoutes.push({
|
|
1332
|
+
filePath: file.path,
|
|
1333
|
+
routePath,
|
|
1334
|
+
httpMethod,
|
|
1335
|
+
decoratorName: `express.${method}`,
|
|
1336
|
+
lineNumber: captureMap['express_route'].startPosition.row + lineOffset,
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
// Extract call sites
|
|
1342
|
+
if (captureMap['call']) {
|
|
1343
|
+
const callNode = captureMap['call'];
|
|
1344
|
+
const callNameNode = captureMap['call.name'];
|
|
1345
|
+
const callExtractor = provider.callExtractor;
|
|
1346
|
+
if (callExtractor) {
|
|
1347
|
+
// ── Path 1: Language-specific call site (bypasses routing) ────
|
|
1348
|
+
// Try language-specific extraction (e.g. Java `::` method references)
|
|
1349
|
+
// without callNameNode. If successful, skip routing and the generic
|
|
1350
|
+
// path entirely.
|
|
1351
|
+
const langCallSite = callExtractor.extract(callNode, undefined);
|
|
1352
|
+
if (langCallSite) {
|
|
1353
|
+
if (!provider.isBuiltInName(langCallSite.calledName)) {
|
|
1354
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1355
|
+
(0, utils_js_1.generateId)('File', file.path);
|
|
1356
|
+
const receiverName = langCallSite.callForm === 'member' ? langCallSite.receiverName : undefined;
|
|
1357
|
+
let receiverTypeName = receiverName
|
|
1358
|
+
? typeEnv.lookup(receiverName, callNode)
|
|
1359
|
+
: undefined;
|
|
1360
|
+
// Type-as-receiver heuristic (e.g. Java `User::getName`)
|
|
1361
|
+
if (langCallSite.typeAsReceiverHeuristic &&
|
|
1362
|
+
receiverName !== undefined &&
|
|
1363
|
+
receiverTypeName === undefined &&
|
|
1364
|
+
langCallSite.callForm === 'member') {
|
|
1365
|
+
const c0 = receiverName.charCodeAt(0);
|
|
1366
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1367
|
+
receiverTypeName = receiverName;
|
|
1368
|
+
}
|
|
1369
|
+
result.calls.push({
|
|
1370
|
+
filePath: file.path,
|
|
1371
|
+
calledName: langCallSite.calledName,
|
|
1372
|
+
sourceId,
|
|
1373
|
+
callForm: langCallSite.callForm,
|
|
1374
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1375
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
// ── Path 2: Generic extraction via @call.name ────────────────
|
|
1381
|
+
if (callNameNode) {
|
|
1382
|
+
const calledName = callNameNode.text;
|
|
1383
|
+
// Dispatch: route language-specific calls (properties, imports).
|
|
1384
|
+
// Call-based heritage (Ruby include/extend/prepend) is no longer
|
|
1385
|
+
// routed here — those calls return 'skip' from the router and the
|
|
1386
|
+
// mixin edges are emitted by scope-resolution (emitHeritageEdges).
|
|
1387
|
+
const routed = callRouter?.(calledName, captureMap['call']);
|
|
1388
|
+
if (routed) {
|
|
1389
|
+
if (routed.kind === 'skip')
|
|
1390
|
+
continue;
|
|
1391
|
+
if (routed.kind === 'import') {
|
|
1392
|
+
// Call-routed imports (e.g. Ruby `require`) are emitted as
|
|
1393
|
+
// IMPORTS edges by the scope-resolution phase; the legacy
|
|
1394
|
+
// per-file extraction that consumed these was removed in
|
|
1395
|
+
// RING4-2 (#943). Skip.
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1398
|
+
if (routed.kind === 'properties') {
|
|
1399
|
+
// #1978: thread the qualifier so a routed property's owner edge
|
|
1400
|
+
// points at the *qualified* nested-class node (Outer.Inner) rather
|
|
1401
|
+
// than a now-nonexistent simple `Class:file:Inner` id. Gated on the
|
|
1402
|
+
// flag → byte-identical when off. Mirrors the main owner path.
|
|
1403
|
+
const propGetQualifiedOwnerName = provider.classExtractor?.qualifiedNodeId === true
|
|
1404
|
+
? (node, simpleName) => provider.classExtractor.extractQualifiedName(node, simpleName)
|
|
1405
|
+
: undefined;
|
|
1406
|
+
const propEnclosingInfo = cachedFindEnclosingClassInfo(captureMap['call'], file.path, provider.resolveEnclosingOwner, propGetQualifiedOwnerName);
|
|
1407
|
+
const propEnclosingClassId = propEnclosingInfo?.qualifiedClassId ?? propEnclosingInfo?.classId ?? null;
|
|
1408
|
+
// Enrich routed properties with FieldExtractor metadata
|
|
1409
|
+
let routedFieldMap;
|
|
1410
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
1411
|
+
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
1412
|
+
if (classNode) {
|
|
1413
|
+
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
1414
|
+
typeEnv,
|
|
1415
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1416
|
+
filePath: file.path,
|
|
1417
|
+
language,
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
for (const item of routed.items) {
|
|
1422
|
+
const routedFieldInfo = routedFieldMap?.get(item.propName);
|
|
1423
|
+
const propQualifiedName = propEnclosingInfo
|
|
1424
|
+
? `${propEnclosingInfo.className}.${item.propName}`
|
|
1425
|
+
: item.propName;
|
|
1426
|
+
const nodeId = (0, utils_js_1.generateId)('Property', `${file.path}:${propQualifiedName}`);
|
|
1427
|
+
result.nodes.push({
|
|
1428
|
+
id: nodeId,
|
|
1429
|
+
label: 'Property',
|
|
1430
|
+
properties: {
|
|
1431
|
+
name: item.propName,
|
|
1432
|
+
filePath: file.path,
|
|
1433
|
+
startLine: item.startLine,
|
|
1434
|
+
endLine: item.endLine,
|
|
1435
|
+
language,
|
|
1436
|
+
isExported: true,
|
|
1437
|
+
description: item.accessorType,
|
|
1438
|
+
...(item.declaredType
|
|
1439
|
+
? { declaredType: item.declaredType }
|
|
1440
|
+
: routedFieldInfo?.type
|
|
1441
|
+
? { declaredType: routedFieldInfo.type }
|
|
1442
|
+
: {}),
|
|
1443
|
+
...(routedFieldInfo?.rawDeclaredType !== undefined
|
|
1444
|
+
? { rawDeclaredType: routedFieldInfo.rawDeclaredType }
|
|
1445
|
+
: {}),
|
|
1446
|
+
...(routedFieldInfo?.annotations !== undefined &&
|
|
1447
|
+
routedFieldInfo.annotations.length > 0
|
|
1448
|
+
? { annotations: routedFieldInfo.annotations }
|
|
1449
|
+
: {}),
|
|
1450
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1451
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1452
|
+
: {}),
|
|
1453
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1454
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1455
|
+
: {}),
|
|
1456
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1457
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1458
|
+
: {}),
|
|
1459
|
+
},
|
|
1460
|
+
});
|
|
1461
|
+
result.symbols.push({
|
|
1462
|
+
filePath: file.path,
|
|
1463
|
+
name: item.propName,
|
|
1464
|
+
nodeId,
|
|
1465
|
+
type: 'Property',
|
|
1466
|
+
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
1467
|
+
...(item.declaredType
|
|
1468
|
+
? { declaredType: item.declaredType }
|
|
1469
|
+
: routedFieldInfo?.type
|
|
1470
|
+
? { declaredType: routedFieldInfo.type }
|
|
1471
|
+
: {}),
|
|
1472
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1473
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1474
|
+
: {}),
|
|
1475
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1476
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1477
|
+
: {}),
|
|
1478
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1479
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1480
|
+
: {}),
|
|
1481
|
+
});
|
|
1482
|
+
// Only emit File -> Property DEFINES for top-level properties
|
|
1483
|
+
// (issue #1944); class members are reached via HAS_PROPERTY.
|
|
1484
|
+
if (!propEnclosingClassId) {
|
|
1485
|
+
const fileId = (0, utils_js_1.generateId)('File', file.path);
|
|
1486
|
+
const relId = (0, utils_js_1.generateId)('DEFINES', `${fileId}->${nodeId}`);
|
|
1487
|
+
result.relationships.push({
|
|
1488
|
+
id: relId,
|
|
1489
|
+
sourceId: fileId,
|
|
1490
|
+
targetId: nodeId,
|
|
1491
|
+
type: 'DEFINES',
|
|
1492
|
+
confidence: 1.0,
|
|
1493
|
+
reason: '',
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
if (propEnclosingClassId) {
|
|
1497
|
+
result.relationships.push({
|
|
1498
|
+
id: (0, utils_js_1.generateId)('HAS_PROPERTY', `${propEnclosingClassId}->${nodeId}`),
|
|
1499
|
+
sourceId: propEnclosingClassId,
|
|
1500
|
+
targetId: nodeId,
|
|
1501
|
+
type: 'HAS_PROPERTY',
|
|
1502
|
+
confidence: 1.0,
|
|
1503
|
+
reason: '',
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
// kind === 'call' — fall through to normal call processing below
|
|
1510
|
+
}
|
|
1511
|
+
if (!provider.isBuiltInName(calledName)) {
|
|
1512
|
+
const callSite = callExtractor.extract(callNode, callNameNode);
|
|
1513
|
+
if (callSite) {
|
|
1514
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1515
|
+
(0, utils_js_1.generateId)('File', file.path);
|
|
1516
|
+
let receiverTypeName = callSite.receiverName
|
|
1517
|
+
? typeEnv.lookup(callSite.receiverName, callNode)
|
|
1518
|
+
: undefined;
|
|
1519
|
+
// Type-as-receiver heuristic
|
|
1520
|
+
if (callSite.typeAsReceiverHeuristic &&
|
|
1521
|
+
callSite.receiverName !== undefined &&
|
|
1522
|
+
receiverTypeName === undefined &&
|
|
1523
|
+
callSite.callForm === 'member') {
|
|
1524
|
+
const c0 = callSite.receiverName.charCodeAt(0);
|
|
1525
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1526
|
+
receiverTypeName = callSite.receiverName;
|
|
1527
|
+
}
|
|
1528
|
+
const inferLiteralType = provider.typeConfig?.inferLiteralType;
|
|
1529
|
+
// Skip when no arg list / zero args: nothing to infer for overload typing
|
|
1530
|
+
const argTypes = inferLiteralType && callSite.argCount !== undefined && callSite.argCount > 0
|
|
1531
|
+
? (0, call_analysis_js_1.extractCallArgTypes)(callNode, inferLiteralType, (varName, cn) => typeEnv.lookup(varName, cn))
|
|
1532
|
+
: undefined;
|
|
1533
|
+
result.calls.push({
|
|
1534
|
+
filePath: file.path,
|
|
1535
|
+
calledName: callSite.calledName,
|
|
1536
|
+
sourceId,
|
|
1537
|
+
...(callSite.argCount !== undefined ? { argCount: callSite.argCount } : {}),
|
|
1538
|
+
...(callSite.callForm !== undefined ? { callForm: callSite.callForm } : {}),
|
|
1539
|
+
...(callSite.receiverName !== undefined
|
|
1540
|
+
? { receiverName: callSite.receiverName }
|
|
1541
|
+
: {}),
|
|
1542
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1543
|
+
...(callSite.receiverMixedChain !== undefined
|
|
1544
|
+
? { receiverMixedChain: callSite.receiverMixedChain }
|
|
1545
|
+
: {}),
|
|
1546
|
+
...(argTypes !== undefined ? { argTypes } : {}),
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
const definitionNode = (0, ast_helpers_js_1.getDefinitionNodeFromCaptures)(captureMap);
|
|
1555
|
+
const defaultNodeLabel = (0, ast_helpers_js_1.getLabelFromCaptures)(captureMap, provider);
|
|
1556
|
+
if (!defaultNodeLabel)
|
|
1557
|
+
continue;
|
|
1558
|
+
const nameNode = captureMap['name'];
|
|
1559
|
+
const extractedClassSymbol = definitionNode && provider.classExtractor?.isTypeDeclaration(definitionNode)
|
|
1560
|
+
? provider.classExtractor.extract(definitionNode, {
|
|
1561
|
+
name: nameNode?.text,
|
|
1562
|
+
type: defaultNodeLabel,
|
|
1563
|
+
})
|
|
1564
|
+
: null;
|
|
1565
|
+
const nodeLabel = extractedClassSymbol?.type ?? defaultNodeLabel;
|
|
1566
|
+
const isClassLikeLabel = nodeLabel === 'Class' ||
|
|
1567
|
+
nodeLabel === 'Struct' ||
|
|
1568
|
+
nodeLabel === 'Interface' ||
|
|
1569
|
+
nodeLabel === 'Enum' ||
|
|
1570
|
+
nodeLabel === 'Record';
|
|
1571
|
+
if (isClassLikeLabel &&
|
|
1572
|
+
provider.classExtractor?.shouldSkipClassCapture?.({
|
|
1573
|
+
captureMap,
|
|
1574
|
+
definitionNode,
|
|
1575
|
+
nameNode,
|
|
1576
|
+
nodeLabel,
|
|
1577
|
+
}) === true) {
|
|
1578
|
+
continue;
|
|
1579
|
+
}
|
|
1580
|
+
const exportDefaultCall = nodeLabel === 'Function' && definitionNode?.type === 'export_statement'
|
|
1581
|
+
? definitionNode.namedChildren.find((child) => child.type === 'call_expression')
|
|
1582
|
+
: undefined;
|
|
1583
|
+
const defaultExportHocName = (() => {
|
|
1584
|
+
if (exportDefaultCall === undefined)
|
|
1585
|
+
return null;
|
|
1586
|
+
const argList = exportDefaultCall.childForFieldName?.('arguments');
|
|
1587
|
+
const callback = argList?.namedChildren.find((child) => child.type === 'arrow_function' || child.type === 'function_expression');
|
|
1588
|
+
if (callback === undefined)
|
|
1589
|
+
return null;
|
|
1590
|
+
const callee = exportDefaultCall.childForFieldName?.('function');
|
|
1591
|
+
if (callee?.type === 'identifier' &&
|
|
1592
|
+
ts_js_hoc_utils_js_1.DEFAULT_EXPORT_IDENTIFIER_BLOCKLIST_SET.has(callee.text))
|
|
1593
|
+
return null;
|
|
1594
|
+
if (callee?.type === 'member_expression') {
|
|
1595
|
+
const property = callee.childForFieldName?.('property');
|
|
1596
|
+
if (property?.type === 'property_identifier' &&
|
|
1597
|
+
ts_js_hoc_utils_js_1.ARRAY_METHOD_HOC_BLOCKLIST_SET.has(property.text))
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
return (0, ts_js_hoc_utils_js_1.deriveDefaultExportHocName)(file.path);
|
|
1601
|
+
})();
|
|
1602
|
+
// `module.exports = function () {}` (#2723): the whole module is the
|
|
1603
|
+
// callable. The member-assignment rule captures the LEFT property as the
|
|
1604
|
+
// name, which here is the literal `exports` — meaningless. Override it:
|
|
1605
|
+
// a named function expression supplies its own name, and the anonymous
|
|
1606
|
+
// forms are named after the file by the same convention anonymous
|
|
1607
|
+
// default exports already use. Takes precedence over `nameNode` for
|
|
1608
|
+
// exactly that reason.
|
|
1609
|
+
//
|
|
1610
|
+
// The derived name is dropped when it COLLIDES with a callable the module
|
|
1611
|
+
// already declares. `format.js` holding `function format() {}` plus an
|
|
1612
|
+
// anonymous `module.exports = function () { return format(v); }` merged
|
|
1613
|
+
// both onto one node, and the inner call to `format` then resolved to
|
|
1614
|
+
// that merged node — fabricating a self-recursion edge present in no
|
|
1615
|
+
// source (#2729 review F4). A fabricated edge is worse than a missing
|
|
1616
|
+
// one: it hands `impact` a caller that does not exist.
|
|
1617
|
+
const isCjsDefaultExport = definitionNode !== undefined && (0, ast_helpers_js_1.isCjsDefaultExportAssignment)(definitionNode);
|
|
1618
|
+
const cjsDefaultExportOwnName = isCjsDefaultExport
|
|
1619
|
+
? definitionNode?.childForFieldName('right')?.childForFieldName('name')?.text
|
|
1620
|
+
: undefined;
|
|
1621
|
+
// A collision must SUPPRESS the definition outright, not merely decline to
|
|
1622
|
+
// name it — falling through would let the captured left property name the
|
|
1623
|
+
// node the literal `exports`, which is both meaningless and the very node
|
|
1624
|
+
// this feature's own test forbids.
|
|
1625
|
+
const suppressCjsDefaultExport = (() => {
|
|
1626
|
+
if (!isCjsDefaultExport || cjsDefaultExportOwnName !== undefined)
|
|
1627
|
+
return false;
|
|
1628
|
+
const root = definitionNode.tree?.rootNode;
|
|
1629
|
+
if (root === undefined)
|
|
1630
|
+
return false;
|
|
1631
|
+
return (0, cjs_export_assignment_js_1.defaultExportNameCollides)(definitionNode, root, (0, ts_js_hoc_utils_js_1.deriveDefaultExportHocName)(file.path));
|
|
1632
|
+
})();
|
|
1633
|
+
if (suppressCjsDefaultExport)
|
|
1634
|
+
continue;
|
|
1635
|
+
const cjsDefaultExportName = isCjsDefaultExport
|
|
1636
|
+
? (cjsDefaultExportOwnName ?? (0, ts_js_hoc_utils_js_1.deriveDefaultExportHocName)(file.path))
|
|
1637
|
+
: null;
|
|
1638
|
+
// Synthesize name for constructors without explicit @name capture (e.g. Swift init)
|
|
1639
|
+
if (!nameNode &&
|
|
1640
|
+
nodeLabel !== 'Constructor' &&
|
|
1641
|
+
!extractedClassSymbol &&
|
|
1642
|
+
!defaultExportHocName &&
|
|
1643
|
+
!cjsDefaultExportName)
|
|
1644
|
+
continue;
|
|
1645
|
+
const nodeName = extractedClassSymbol?.name ??
|
|
1646
|
+
defaultExportHocName ??
|
|
1647
|
+
cjsDefaultExportName ??
|
|
1648
|
+
(nameNode ? nameNode.text : 'init');
|
|
1649
|
+
// Dedup: variable captures (Const/Static/Variable) may overlap with higher-priority
|
|
1650
|
+
// captures (e.g. `const fn = () => {}` matches both @definition.function and @definition.const).
|
|
1651
|
+
// Multi-name declarations share the same definition node, so include the emitted name.
|
|
1652
|
+
//
|
|
1653
|
+
// `processedDefinitionNodes` alone only suppressed the value twin when the
|
|
1654
|
+
// function-like match happened to be processed FIRST — and it is not.
|
|
1655
|
+
// tree-sitter completes `@definition.const` at `@name`, while
|
|
1656
|
+
// `@definition.function` must also match the trailing arrow / function
|
|
1657
|
+
// expression, so the const match is yielded first and its edgeless twin
|
|
1658
|
+
// escaped (#2687). `definitionPreScan` is the order-independent view of
|
|
1659
|
+
// the same claim, pre-scanned over `matches` before this loop and ranked so
|
|
1660
|
+
// a capture is dropped only by a STRICTLY higher-ranked claimant.
|
|
1661
|
+
//
|
|
1662
|
+
// It also replaces the old bare-`startIndex` claim, which was too coarse:
|
|
1663
|
+
// a callable declared FIRST in a multi-name declaration
|
|
1664
|
+
// (`const cb = () => 1, SIBLING = 2`) registered the shared definition
|
|
1665
|
+
// node and silently dropped every later sibling on it. Both keys are now
|
|
1666
|
+
// name-scoped, so siblings survive in either declarator order.
|
|
1667
|
+
//
|
|
1668
|
+
// The long-term collapse seam for this duplicate class is
|
|
1669
|
+
// `selectNodeBearingDef` (#1876, still unwired); this pre-scan is the local
|
|
1670
|
+
// form that keeps the hot loop single-pass. Keep them in sync if #1876 lands.
|
|
1671
|
+
if (definitionNode) {
|
|
1672
|
+
const definitionNameKey = `${definitionNode.startIndex}:${nodeName}`;
|
|
1673
|
+
if ((0, ast_helpers_js_1.isValueDefinitionLabel)(nodeLabel)) {
|
|
1674
|
+
if (processedDefinitionNodes.has(definitionNameKey) ||
|
|
1675
|
+
definitionPreScan.nonValue.has(definitionNameKey)) {
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
processedDefinitionNodes.add(definitionNameKey);
|
|
1679
|
+
}
|
|
1680
|
+
else if (nodeLabel === 'Property' && definitionPreScan.callable.has(definitionNameKey)) {
|
|
1681
|
+
// Only a CALLABLE collapses a property. Consulting the wider
|
|
1682
|
+
// `nonValue` set here would let a property suppress itself, and would
|
|
1683
|
+
// let an annotated Python attribute lose to its own bare-assignment
|
|
1684
|
+
// twin — the property must outrank `Variable`, not tie with it.
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
// #2735: for a bound callable the graph-node capture sits on the OUTER
|
|
1689
|
+
// wrapper while scope-resolution anchors on the INNER expression. The
|
|
1690
|
+
// position join is line-only, so `startLine` must follow the initializer
|
|
1691
|
+
// (ids still use `definitionNode` via `localIdentity`).
|
|
1692
|
+
const startRow = definitionNode &&
|
|
1693
|
+
(nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor')
|
|
1694
|
+
? (0, callable_id_js_1.boundCallableStartRow)(definitionNode, nodeName, nodeLabel, parsedFile?.localDefs, nameNode)
|
|
1695
|
+
: definitionNode?.startPosition.row;
|
|
1696
|
+
const startLine = startRow !== undefined
|
|
1697
|
+
? startRow + lineOffset
|
|
1698
|
+
: nameNode
|
|
1699
|
+
? nameNode.startPosition.row + lineOffset
|
|
1700
|
+
: lineOffset;
|
|
1701
|
+
// Compute enclosing class BEFORE node ID — needed to qualify method IDs
|
|
1702
|
+
const needsOwner = nodeLabel === 'Method' ||
|
|
1703
|
+
nodeLabel === 'Constructor' ||
|
|
1704
|
+
nodeLabel === 'Property' ||
|
|
1705
|
+
nodeLabel === 'Function';
|
|
1706
|
+
// #1978: thread the class-extractor's qualifier into the owner walk when the
|
|
1707
|
+
// language opts into qualified node ids, so a nested member's owner resolves
|
|
1708
|
+
// to the *qualified* class id (Outer.Inner). Gated on the flag → byte-identical
|
|
1709
|
+
// when off. Mirrors parsing-processor.ts.
|
|
1710
|
+
const getQualifiedOwnerName = provider.classExtractor?.qualifiedNodeId === true
|
|
1711
|
+
? (node, simpleName) =>
|
|
1712
|
+
// #1991: LOCKSTEP — a Ruby `module` owner is not a typeDeclaration, so
|
|
1713
|
+
// extractQualifiedName returns null; fall back to the scope walk so a
|
|
1714
|
+
// method inside a nested module owns through the SAME qualified Trait
|
|
1715
|
+
// id its node uses on the worker path too.
|
|
1716
|
+
provider.classExtractor.extractQualifiedName(node, simpleName) ??
|
|
1717
|
+
provider.classExtractor.qualifyScopeName?.(node, simpleName) ??
|
|
1718
|
+
null
|
|
1719
|
+
: undefined;
|
|
1720
|
+
// A Property declared inside a function/lambda BODY is a function-LOCAL
|
|
1721
|
+
// binding (e.g. Kotlin `val (a,b) = pair` or a `for ((k,v) in m)` loop
|
|
1722
|
+
// destructuring emitted as `@definition.property` to dodge the local-symbol
|
|
1723
|
+
// pruner), NOT a class member. Such locals must not get a HAS_PROPERTY owner
|
|
1724
|
+
// edge from the enclosing class. Detect them by walking from the def node:
|
|
1725
|
+
// if a function-like ancestor is reached BEFORE any class container, the
|
|
1726
|
+
// property is enclosed by a function. Language-agnostic — genuine class
|
|
1727
|
+
// fields sit directly in the class body with no intervening function, so
|
|
1728
|
+
// they are unaffected (#1919 review CF3).
|
|
1729
|
+
//
|
|
1730
|
+
// EXCEPTION: a constructor PARAMETER property (TypeScript
|
|
1731
|
+
// `constructor(public name: string)`) is also enclosed by a function, but
|
|
1732
|
+
// it IS a class member — it is reached through the parameter list, not the
|
|
1733
|
+
// executable body. So only strip the owner when the property is NOT inside
|
|
1734
|
+
// a parameter list of that function (i.e. it's a body local).
|
|
1735
|
+
const propOwnerNode = nameNode || definitionNode;
|
|
1736
|
+
// A Property is function-local (and must NOT get a class HAS_PROPERTY owner)
|
|
1737
|
+
// when its nearest enclosing executable body — reached before any class
|
|
1738
|
+
// container — is a function/accessor/initializer body, AND it is not a
|
|
1739
|
+
// constructor parameter-property (rescued by the param-list carve-out).
|
|
1740
|
+
// Uses LOCAL_SCOPE_BODY_NODE_TYPES (not FUNCTION_NODE_TYPES): the latter
|
|
1741
|
+
// mis-includes Dart bare signatures (over-stripping accessors) and omits
|
|
1742
|
+
// Kotlin/Swift init+accessor bodies (under-stripping their locals) — see
|
|
1743
|
+
// the #1919 review of this guard.
|
|
1744
|
+
const isFunctionLocalProperty = nodeLabel === 'Property' &&
|
|
1745
|
+
propOwnerNode !== undefined &&
|
|
1746
|
+
(0, ast_helpers_js_1.findAncestorBeforeBoundary)(propOwnerNode, ast_helpers_js_1.LOCAL_SCOPE_BODY_NODE_TYPES, ast_helpers_js_1.CLASS_CONTAINER_TYPES) !== null &&
|
|
1747
|
+
(0, ast_helpers_js_1.findAncestorBeforeBoundary)(propOwnerNode, ast_helpers_js_1.PARAMETER_LIST_NODE_TYPES, ast_helpers_js_1.LOCAL_SCOPE_BODY_NODE_TYPES) === null;
|
|
1748
|
+
const enclosingClassInfo = needsOwner && !isFunctionLocalProperty
|
|
1749
|
+
? cachedFindEnclosingClassInfo(nameNode || definitionNode, file.path, provider.resolveEnclosingOwner, getQualifiedOwnerName)
|
|
1750
|
+
: null;
|
|
1751
|
+
const enclosingClassId = enclosingClassInfo?.qualifiedClassId ?? enclosingClassInfo?.classId ?? null;
|
|
1752
|
+
// A Method with no enclosing class container is owned by a NAMED binding
|
|
1753
|
+
// instead: an object literal (`const service = { load() {} }`) or, since
|
|
1754
|
+
// the #2723 follow-up, a prototype assignment
|
|
1755
|
+
// (`Foo.prototype.bar = function () {}`). Both resolve the owner from the
|
|
1756
|
+
// syntax rather than from an ancestor walk, and both are language-shaped
|
|
1757
|
+
// helpers behind the provider's own label decision — shared code here
|
|
1758
|
+
// only asks "does this Method name an owner".
|
|
1759
|
+
const objectLiteralOwnerInfo = !enclosingClassId && nodeLabel === 'Method' && definitionNode
|
|
1760
|
+
? ((0, ast_helpers_js_1.findMemberAssignmentOwnerInfo)(definitionNode, file.path) ??
|
|
1761
|
+
(0, ast_helpers_js_1.findObjectLiteralBindingInfo)(definitionNode, file.path))
|
|
1762
|
+
: null;
|
|
1763
|
+
// #1978: hoisted ABOVE qualifiedName/node-id (load-bearing order) so a
|
|
1764
|
+
// class-like node can key its id by its fully-qualified path. Derived from
|
|
1765
|
+
// the SAME extractQualifiedName the owner edge uses → owner id == node id.
|
|
1766
|
+
const classNodeForSymbol = definitionNode || nameNode;
|
|
1767
|
+
const qualifiedTypeName = extractedClassSymbol?.qualifiedName ??
|
|
1768
|
+
(classNodeForSymbol && provider.classExtractor?.isTypeDeclaration(classNodeForSymbol)
|
|
1769
|
+
? (provider.classExtractor.extractQualifiedName(classNodeForSymbol, nodeName) ?? nodeName)
|
|
1770
|
+
: // #1991: LOCKSTEP with parsing-processor.ts — qualify a Ruby `module`
|
|
1771
|
+
// (Trait) via the scope walk so same-tail nested mixin modules get
|
|
1772
|
+
// distinct ids on the worker path too. Gated on qualifiedNodeId.
|
|
1773
|
+
(0, ast_helpers_js_1.isQualifiableScopeLabel)(nodeLabel) &&
|
|
1774
|
+
provider.classExtractor?.qualifiedNodeId === true &&
|
|
1775
|
+
classNodeForSymbol
|
|
1776
|
+
? (provider.classExtractor.qualifyScopeName?.(classNodeForSymbol, nodeName) ??
|
|
1777
|
+
undefined)
|
|
1778
|
+
: undefined);
|
|
1779
|
+
// Qualify method/property IDs with enclosing class name to avoid collisions.
|
|
1780
|
+
// Class-like nodes use their own fully-qualified path as the id key when the
|
|
1781
|
+
// language enables qualifiedNodeId (#1978); everything else is unchanged.
|
|
1782
|
+
// #1982: LOCKSTEP with parsing-processor.ts — a Rust inherent-impl with an
|
|
1783
|
+
// UNSCOPED bare target is keyed by the enclosing `mod_item` scope so the
|
|
1784
|
+
// worker-path Impl node id matches the sequential path and the owner walk.
|
|
1785
|
+
// #2699: a callable nested inside another callable is qualified by the
|
|
1786
|
+
// enclosing callable, so a function-local closure stops colliding with a
|
|
1787
|
+
// same-named file-level function.
|
|
1788
|
+
// Applies to VALUES as well as callables since #2699 closed A1: a
|
|
1789
|
+
// top-level `const handler` and a function-local `const handler`
|
|
1790
|
+
// otherwise collapse onto one `Const:v.ts:handler`, which was the
|
|
1791
|
+
// issue's original complaint and is unreachable from a callable-only
|
|
1792
|
+
// gate. `isPositionQualifiedLocalLabel` is the single definition of that
|
|
1793
|
+
// set, shared with resolution in `ids.ts` — the two phases disagreeing
|
|
1794
|
+
// silently drops edges rather than failing (#2714).
|
|
1795
|
+
// Same helper as the caller-attribution phase — see `enclosingCallablePrefix`.
|
|
1796
|
+
//
|
|
1797
|
+
// A CLASS MEMBER must never gain a local prefix, and the plain walk is not
|
|
1798
|
+
// enough to guarantee that once `Property`/`Static` are in the set. A
|
|
1799
|
+
// TypeScript constructor PARAMETER PROPERTY —
|
|
1800
|
+
// `constructor(private readonly port: Port)` — reaches the constructor's
|
|
1801
|
+
// `method_definition` THROUGH the parameter list, and `method_definition`
|
|
1802
|
+
// is in LOCAL_SCOPE_BODY_NODE_TYPES, so the walk hits it BEFORE any class
|
|
1803
|
+
// boundary and re-keys a genuine field as `C.constructor.port@r:c`. That
|
|
1804
|
+
// silently empties the `C.port` slot every `impact` / `rename` / FTS
|
|
1805
|
+
// consumer addresses, and the class still asserts HAS_PROPERTY against it.
|
|
1806
|
+
//
|
|
1807
|
+
// `isFunctionLocalProperty` above already encodes the correct test (a
|
|
1808
|
+
// parameter-list ancestor reached before any local-scope body means NOT
|
|
1809
|
+
// function-local); reuse that exclusion here rather than spelling a second
|
|
1810
|
+
// rule, so the owner-edge decision and the id decision cannot disagree.
|
|
1811
|
+
const isParameterScopedMember = (nodeLabel === 'Property' || nodeLabel === 'Static') &&
|
|
1812
|
+
definitionNode !== undefined &&
|
|
1813
|
+
(0, ast_helpers_js_1.findAncestorBeforeBoundary)(definitionNode, ast_helpers_js_1.PARAMETER_LIST_NODE_TYPES, ast_helpers_js_1.LOCAL_SCOPE_BODY_NODE_TYPES) !== null;
|
|
1814
|
+
const nestedCallablePrefix = (0, callable_labels_js_1.isPositionQualifiedLocalLabel)(nodeLabel) && definitionNode && !isParameterScopedMember
|
|
1815
|
+
? enclosingCallablePrefix(definitionNode, file.path, provider)
|
|
1816
|
+
: undefined;
|
|
1817
|
+
const rustImplQualifiedName = nodeLabel === 'Impl' &&
|
|
1818
|
+
definitionNode?.type === 'impl_item' &&
|
|
1819
|
+
nameNode?.type === 'type_identifier'
|
|
1820
|
+
? (0, ast_helpers_js_1.qualifyRustImplTargetByModScope)(definitionNode, nodeName)
|
|
1821
|
+
: undefined;
|
|
1822
|
+
const qualifiedNameBeforeModScope = rustImplQualifiedName !== undefined
|
|
1823
|
+
? rustImplQualifiedName
|
|
1824
|
+
: // #1991: LOCKSTEP — include Trait so a Ruby mixin module's qualified
|
|
1825
|
+
// scope id keys the worker-path node, matching the sequential path.
|
|
1826
|
+
(isClassLikeLabel || (0, ast_helpers_js_1.isQualifiableScopeLabel)(nodeLabel)) &&
|
|
1827
|
+
provider.classExtractor?.qualifiedNodeId === true &&
|
|
1828
|
+
qualifiedTypeName !== undefined
|
|
1829
|
+
? qualifiedTypeName
|
|
1830
|
+
: nestedCallablePrefix !== undefined && definitionNode
|
|
1831
|
+
? (0, callable_id_js_1.nestedCallableQualifiedName)(nestedCallablePrefix, definitionNode, nodeName)
|
|
1832
|
+
: enclosingClassInfo
|
|
1833
|
+
? `${enclosingClassInfo.className}.${nodeName}`
|
|
1834
|
+
: // A member whose owner is named by the assignment rather than
|
|
1835
|
+
// by an enclosing container (`Foo.prototype.bar = …`) qualifies
|
|
1836
|
+
// by that owner, so two constructors in one file that both
|
|
1837
|
+
// define `bar` stay distinct nodes.
|
|
1838
|
+
objectLiteralOwnerInfo?.ownerName !== undefined
|
|
1839
|
+
? `${objectLiteralOwnerInfo.ownerName}.${nodeName}`
|
|
1840
|
+
: nodeName;
|
|
1841
|
+
// #2742: qualify by the enclosing `mod` chain, so two same-named items at
|
|
1842
|
+
// different module depths in one file are DISTINCT nodes. Without this,
|
|
1843
|
+
// `mod inner { fn dispatch }` and a crate-root `fn dispatch` both keyed
|
|
1844
|
+
// `Function:<file>:dispatch`, first-wins — so a correctly resolved call
|
|
1845
|
+
// into the inline module still rendered as a self-loop, and `impact`
|
|
1846
|
+
// reported the real callee as unreached.
|
|
1847
|
+
//
|
|
1848
|
+
// Keyed purely on the `mod_item` node type, exactly as the impl-target
|
|
1849
|
+
// qualifier above (#1982) already is, so it is a no-op for every language
|
|
1850
|
+
// whose grammar has no such node. The impl branch already applied it and
|
|
1851
|
+
// is left alone rather than qualified twice.
|
|
1852
|
+
//
|
|
1853
|
+
// Scoped to items that sit on NEITHER side of an owner edge, because only
|
|
1854
|
+
// the id is re-keyed here — the anchor is minted independently by
|
|
1855
|
+
// `findEnclosingClassInfo` and does not move with it:
|
|
1856
|
+
//
|
|
1857
|
+
// - `!enclosingClassInfo` excludes the MEMBER side. A method already
|
|
1858
|
+
// carries its owner's name (`Inner.method`), and for an unscoped
|
|
1859
|
+
// inherent impl that owner is mod-scoped by the impl qualifier above,
|
|
1860
|
+
// so qualifying the member again would break the byte-for-byte
|
|
1861
|
+
// agreement #1975/#1982 established.
|
|
1862
|
+
// - `!MEMBER_OWNER_NODE_TYPES.has(...)` excludes the OWNER side. A
|
|
1863
|
+
// `struct` / `trait` / `enum` / `impl` declared directly in a `mod` has
|
|
1864
|
+
// no enclosing class, so the member-side guard alone let it through
|
|
1865
|
+
// while its own anchor stayed bare — `mod engine { struct Config { … } }`
|
|
1866
|
+
// minted `Struct:<file>:engine.Config` against a `HAS_PROPERTY` edge
|
|
1867
|
+
// anchored on `Struct:<file>:Config`, dangling every field. The same
|
|
1868
|
+
// gap put `impl a::Inner` inside a `mod` back on the #1975 rake this
|
|
1869
|
+
// helper's own docblock warns about: the impl branch above deliberately
|
|
1870
|
+
// fires only for an UNSCOPED `type_identifier`, and this gate was
|
|
1871
|
+
// picking up the scoped targets it had just excluded.
|
|
1872
|
+
// - `nestedCallablePrefix === undefined` excludes an item inside a
|
|
1873
|
+
// CALLABLE. `fn wrapper() { mod helper { fn dispatch } }` composed the
|
|
1874
|
+
// mod segment outermost — `helper.wrapper.dispatch@2:8` — inverting the
|
|
1875
|
+
// real nesting, because the mod prefix is prepended to a name the
|
|
1876
|
+
// enclosing-callable pass has already qualified. Nothing dangled (the
|
|
1877
|
+
// `@line:col` suffix keeps such ids unique on its own, which is also why
|
|
1878
|
+
// the mod segment adds no identity here), but the path read as a lie
|
|
1879
|
+
// about the source. Skipping is the honest answer; reordering would mean
|
|
1880
|
+
// interleaving two qualifier passes for a shape that only ever produces
|
|
1881
|
+
// already-unique ids.
|
|
1882
|
+
//
|
|
1883
|
+
// Same-named members on same-named types in sibling modules therefore
|
|
1884
|
+
// still collapse, as do the containers themselves — unchanged from before
|
|
1885
|
+
// this fix, and owned by the owner edge rather than worked around here.
|
|
1886
|
+
const qualifiesByEnclosingModScope = rustImplQualifiedName === undefined &&
|
|
1887
|
+
definitionNode !== undefined &&
|
|
1888
|
+
!ast_helpers_js_1.MEMBER_OWNER_NODE_TYPES.has(definitionNode.type) &&
|
|
1889
|
+
nestedCallablePrefix === undefined &&
|
|
1890
|
+
!enclosingClassInfo &&
|
|
1891
|
+
objectLiteralOwnerInfo?.ownerName === undefined;
|
|
1892
|
+
const qualifiedName = qualifiesByEnclosingModScope && definitionNode !== undefined
|
|
1893
|
+
? (0, ast_helpers_js_1.qualifyByEnclosingModScope)(definitionNode, qualifiedNameBeforeModScope)
|
|
1894
|
+
: qualifiedNameBeforeModScope;
|
|
1895
|
+
// Extract method metadata BEFORE generating node ID — parameterCount is needed
|
|
1896
|
+
// to disambiguate overloaded methods via #<arity> suffix in the ID.
|
|
1897
|
+
let declaredType;
|
|
1898
|
+
let methodProps = {};
|
|
1899
|
+
let arityForId; // raw param count for ID, even for variadic
|
|
1900
|
+
let defMethodMap;
|
|
1901
|
+
let defMethodInfo;
|
|
1902
|
+
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
1903
|
+
// Use MethodExtractor for method metadata — provides parameterCount, parameterTypes,
|
|
1904
|
+
// returnType, isAbstract/isFinal/annotations, visibility, and more.
|
|
1905
|
+
let enrichedByMethodExtractor = false;
|
|
1906
|
+
if (provider.methodExtractor && definitionNode) {
|
|
1907
|
+
const classNode = findEnclosingClassNode(definitionNode) ?? findClassNodeByQualifiedName(definitionNode);
|
|
1908
|
+
if (classNode) {
|
|
1909
|
+
const methodMap = getMethodInfo(classNode, provider, {
|
|
1910
|
+
filePath: file.path,
|
|
1911
|
+
language,
|
|
1912
|
+
});
|
|
1913
|
+
const defLine = definitionNode.startPosition.row + 1;
|
|
1914
|
+
const info = methodMap?.get(`${nodeName}:${defLine}`);
|
|
1915
|
+
if (info) {
|
|
1916
|
+
enrichedByMethodExtractor = true;
|
|
1917
|
+
arityForId = (0, method_props_js_1.arityForIdFromInfo)(info);
|
|
1918
|
+
methodProps = (0, method_props_js_1.buildMethodProps)(info);
|
|
1919
|
+
defMethodMap = methodMap;
|
|
1920
|
+
defMethodInfo = info;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
// For top-level methods (e.g. Go method_declaration), try extractFromNode
|
|
1925
|
+
if (!enrichedByMethodExtractor &&
|
|
1926
|
+
provider.methodExtractor?.extractFromNode &&
|
|
1927
|
+
definitionNode) {
|
|
1928
|
+
const info = provider.methodExtractor.extractFromNode(definitionNode, {
|
|
1929
|
+
filePath: file.path,
|
|
1930
|
+
language,
|
|
1931
|
+
});
|
|
1932
|
+
if (info) {
|
|
1933
|
+
enrichedByMethodExtractor = true;
|
|
1934
|
+
arityForId = (0, method_props_js_1.arityForIdFromInfo)(info);
|
|
1935
|
+
methodProps = (0, method_props_js_1.buildMethodProps)(info);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
// Append #<paramCount> to owned callable IDs to disambiguate overloads.
|
|
1940
|
+
// Top-level Function IDs stay stable; functions inside an owner may overload.
|
|
1941
|
+
// When same-arity collisions exist, append ~type1,type2 for further disambiguation.
|
|
1942
|
+
const needsAritySuffix = nodeLabel === 'Method' ||
|
|
1943
|
+
nodeLabel === 'Constructor' ||
|
|
1944
|
+
(nodeLabel === 'Function' && enclosingClassId !== null);
|
|
1945
|
+
let arityTag = needsAritySuffix && arityForId !== undefined ? `#${arityForId}` : '';
|
|
1946
|
+
if (arityTag && defMethodMap && defMethodInfo) {
|
|
1947
|
+
const groups = (0, method_props_js_1.buildCollisionGroups)(defMethodMap);
|
|
1948
|
+
arityTag += (0, method_props_js_1.typeTagForId)(defMethodMap, nodeName, arityForId, defMethodInfo, language, groups);
|
|
1949
|
+
arityTag += (0, method_props_js_1.constTagForId)(defMethodMap, nodeName, arityForId, defMethodInfo, groups);
|
|
1950
|
+
}
|
|
1951
|
+
const parameterShapeTag = nodeLabel === 'Function' || nodeLabel === 'Method'
|
|
1952
|
+
? (0, method_props_js_1.parameterShapeIdTag)(methodProps.parameterTypes, methodProps.parameterTypeClasses)
|
|
1953
|
+
: '';
|
|
1954
|
+
const classTemplateArguments = extractedClassSymbol?.templateArguments ??
|
|
1955
|
+
provider.classExtractor?.extractTemplateArgumentsFromCapture?.({
|
|
1956
|
+
captureMap,
|
|
1957
|
+
definitionNode,
|
|
1958
|
+
nameNode,
|
|
1959
|
+
}) ??
|
|
1960
|
+
(captureMap['template-arguments']
|
|
1961
|
+
? (0, template_arguments_js_1.extractTemplateArguments)(captureMap['template-arguments'].text)
|
|
1962
|
+
: undefined) ??
|
|
1963
|
+
(nameNode && nameNode.text ? (0, template_arguments_js_1.extractTemplateArguments)(nameNode.text) : undefined);
|
|
1964
|
+
const classTemplateTag = (nodeLabel === 'Class' ||
|
|
1965
|
+
nodeLabel === 'Struct' ||
|
|
1966
|
+
nodeLabel === 'Interface' ||
|
|
1967
|
+
nodeLabel === 'Enum' ||
|
|
1968
|
+
nodeLabel === 'Record') &&
|
|
1969
|
+
classTemplateArguments !== undefined &&
|
|
1970
|
+
classTemplateArguments.length > 0
|
|
1971
|
+
? (0, template_arguments_js_1.templateArgumentsIdTag)(classTemplateArguments)
|
|
1972
|
+
: '';
|
|
1973
|
+
// SFINAE / `requires`-clause aware ID disambiguation (issue #1579).
|
|
1974
|
+
// Function-template overloads with identical parameterTypes but
|
|
1975
|
+
// mutually-exclusive constraints (e.g. `enable_if_t<is_integral_v<T>>`
|
|
1976
|
+
// vs `enable_if_t<is_floating_point_v<T>>`) need distinct graph nodes
|
|
1977
|
+
// so the constraint-filter step in `narrowOverloadCandidates` has two
|
|
1978
|
+
// candidates to narrow between. Without this tag they collapse to a
|
|
1979
|
+
// single Function node and the SFINAE call resolves to only one edge
|
|
1980
|
+
// regardless of which overload's constraint holds. This mirrors the
|
|
1981
|
+
// sequential `parsing-processor` path removed in #1983 — the worker is
|
|
1982
|
+
// now the sole parse path, so it must stamp the constraint tag and the
|
|
1983
|
+
// `templateConstraints` node property the resolver looks up by re-
|
|
1984
|
+
// hashing the def's constraints (see graph-bridge ids.ts / node-lookup.ts).
|
|
1985
|
+
let parsedTemplateConstraints = undefined;
|
|
1986
|
+
let constraintsTag = '';
|
|
1987
|
+
if ((nodeLabel === 'Function' || nodeLabel === 'Method') &&
|
|
1988
|
+
provider.extractTemplateConstraints !== undefined &&
|
|
1989
|
+
definitionNode) {
|
|
1990
|
+
try {
|
|
1991
|
+
parsedTemplateConstraints = provider.extractTemplateConstraints(definitionNode);
|
|
1992
|
+
if (parsedTemplateConstraints !== undefined) {
|
|
1993
|
+
constraintsTag = (0, template_arguments_js_1.templateConstraintsIdTag)(parsedTemplateConstraints);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
catch (err) {
|
|
1997
|
+
// Optional C++ template-constraint enrichment: fall back to no tag, but
|
|
1998
|
+
// surface the failure (#2264) — matches the CFG-build warning above.
|
|
1999
|
+
reportWarning(`Template-constraint extraction failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2000
|
+
parsedTemplateConstraints = undefined;
|
|
2001
|
+
constraintsTag = '';
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
const nodeId = (0, utils_js_1.generateId)(nodeLabel, `${file.path}:${qualifiedName}${classTemplateTag}${arityTag}${parameterShapeTag}${constraintsTag}`);
|
|
2005
|
+
let description;
|
|
2006
|
+
try {
|
|
2007
|
+
description = provider.descriptionExtractor?.(nodeLabel, nodeName, captureMap);
|
|
2008
|
+
}
|
|
2009
|
+
catch (err) {
|
|
2010
|
+
// A throw here (an unexpected tree-sitter node shape, a provider bug) must
|
|
2011
|
+
// NOT propagate — it would escape processFileGroup to the language-group
|
|
2012
|
+
// catch, which treats any throw as "parser unavailable" and silently drops
|
|
2013
|
+
// every remaining file in the group. Mirrors the extractTemplateConstraints
|
|
2014
|
+
// guard above (#2286 review).
|
|
2015
|
+
reportWarning(`Description extraction failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2016
|
+
description = undefined;
|
|
2017
|
+
}
|
|
2018
|
+
let frameworkHint = definitionNode
|
|
2019
|
+
? (0, framework_detection_js_1.detectFrameworkFromAST)(language, (definitionNode.text || '').slice(0, 300))
|
|
2020
|
+
: null;
|
|
2021
|
+
// Suppress Spring framework hint for methods inside interfaces
|
|
2022
|
+
// (Feign clients, JAX-RS proxies are consumers, not providers)
|
|
2023
|
+
if (frameworkHint && definitionNode) {
|
|
2024
|
+
let classCheck = definitionNode.parent;
|
|
2025
|
+
while (classCheck) {
|
|
2026
|
+
if (classCheck.type === 'interface_declaration') {
|
|
2027
|
+
frameworkHint = null;
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
if (classCheck.type === 'class_declaration' || classCheck.type === 'program') {
|
|
2031
|
+
break;
|
|
2032
|
+
}
|
|
2033
|
+
classCheck = classCheck.parent;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
// Decorators appear on lines immediately before their definition; allow up to
|
|
2037
|
+
// MAX_DECORATOR_SCAN_LINES gap for blank lines / multi-line decorator stacks.
|
|
2038
|
+
const MAX_DECORATOR_SCAN_LINES = 5;
|
|
2039
|
+
if (definitionNode) {
|
|
2040
|
+
const defStartLine = definitionNode.startPosition.row;
|
|
2041
|
+
for (let checkLine = defStartLine - 1; checkLine >= Math.max(0, defStartLine - MAX_DECORATOR_SCAN_LINES); checkLine--) {
|
|
2042
|
+
const dec = fileDecorators.get(checkLine);
|
|
2043
|
+
if (dec) {
|
|
2044
|
+
// Use first (closest) decorator found for framework hint
|
|
2045
|
+
if (!frameworkHint) {
|
|
2046
|
+
frameworkHint = {
|
|
2047
|
+
framework: 'decorator',
|
|
2048
|
+
entryPointMultiplier: 1.2,
|
|
2049
|
+
reason: `@${dec.name}${dec.arg ? `("${dec.arg}")` : ''}`,
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
// Emit tool definition if this is a @tool decorator
|
|
2053
|
+
if (dec.isTool) {
|
|
2054
|
+
result.toolDefs.push({
|
|
2055
|
+
filePath: file.path,
|
|
2056
|
+
toolName: nodeName,
|
|
2057
|
+
description: (dec.arg || description || '').slice(0, 200),
|
|
2058
|
+
lineNumber: definitionNode.startPosition.row + lineOffset,
|
|
2059
|
+
handlerNodeId: nodeId,
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
fileDecorators.delete(checkLine);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
// Property metadata extraction (not needed before nodeId — Properties don't overload)
|
|
2067
|
+
if (nodeLabel === 'Property' && definitionNode) {
|
|
2068
|
+
// FieldExtractor is the single source of truth when available
|
|
2069
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
2070
|
+
const classNode = findEnclosingClassNode(definitionNode);
|
|
2071
|
+
if (classNode) {
|
|
2072
|
+
const fieldMap = getFieldInfo(classNode, provider, {
|
|
2073
|
+
typeEnv,
|
|
2074
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
2075
|
+
filePath: file.path,
|
|
2076
|
+
language,
|
|
2077
|
+
});
|
|
2078
|
+
const info = fieldMap?.get(nodeName);
|
|
2079
|
+
if (info) {
|
|
2080
|
+
declaredType = info.type ?? undefined;
|
|
2081
|
+
// Mutate methodProps BEFORE the `{...methodProps}` spread below —
|
|
2082
|
+
// rawDeclaredType is the verbatim generic type text (U1, PR #2200).
|
|
2083
|
+
if (info.rawDeclaredType !== undefined) {
|
|
2084
|
+
methodProps.rawDeclaredType = info.rawDeclaredType;
|
|
2085
|
+
}
|
|
2086
|
+
// Field annotations ('@Name' strings, U2 PR #2200) — omit when empty.
|
|
2087
|
+
if (info.annotations !== undefined && info.annotations.length > 0) {
|
|
2088
|
+
methodProps.annotations = info.annotations;
|
|
2089
|
+
}
|
|
2090
|
+
methodProps.visibility = info.visibility;
|
|
2091
|
+
methodProps.isStatic = info.isStatic;
|
|
2092
|
+
methodProps.isReadonly = info.isReadonly;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
// Variable/Const/Static metadata extraction via VariableExtractor
|
|
2098
|
+
if ((nodeLabel === 'Const' || nodeLabel === 'Static' || nodeLabel === 'Variable') &&
|
|
2099
|
+
definitionNode &&
|
|
2100
|
+
provider.variableExtractor) {
|
|
2101
|
+
let variableInfoByName = variableInfoCache.get(definitionNode.startIndex);
|
|
2102
|
+
if (!variableInfoByName) {
|
|
2103
|
+
const varCtx = {
|
|
2104
|
+
filePath: file.path,
|
|
2105
|
+
language,
|
|
2106
|
+
};
|
|
2107
|
+
variableInfoByName = new Map(provider.variableExtractor
|
|
2108
|
+
.extractAll(definitionNode, varCtx)
|
|
2109
|
+
.map((info) => [info.name, info]));
|
|
2110
|
+
variableInfoCache.set(definitionNode.startIndex, variableInfoByName);
|
|
2111
|
+
}
|
|
2112
|
+
const varInfo = variableInfoByName.get(nodeName);
|
|
2113
|
+
if (varInfo) {
|
|
2114
|
+
if (varInfo.type)
|
|
2115
|
+
declaredType = varInfo.type;
|
|
2116
|
+
methodProps.visibility = varInfo.visibility;
|
|
2117
|
+
methodProps.isStatic = varInfo.isStatic;
|
|
2118
|
+
methodProps.isConst = varInfo.isConst;
|
|
2119
|
+
methodProps.isMutable = varInfo.isMutable;
|
|
2120
|
+
methodProps.scope = varInfo.scope;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
result.nodes.push({
|
|
2124
|
+
id: nodeId,
|
|
2125
|
+
label: nodeLabel,
|
|
2126
|
+
properties: {
|
|
2127
|
+
name: nodeName,
|
|
2128
|
+
filePath: file.path,
|
|
2129
|
+
startLine,
|
|
2130
|
+
endLine: definitionNode ? definitionNode.endPosition.row + lineOffset : startLine,
|
|
2131
|
+
language: language,
|
|
2132
|
+
isExported: language === index_js_1.SupportedLanguages.Vue && isVueSetup
|
|
2133
|
+
? (0, vue_sfc_extractor_js_1.isVueSetupTopLevel)(nameNode || definitionNode)
|
|
2134
|
+
: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
|
|
2135
|
+
...(qualifiedTypeName !== undefined ? { qualifiedName: qualifiedTypeName } : {}),
|
|
2136
|
+
...(classTemplateArguments !== undefined && classTemplateArguments.length > 0
|
|
2137
|
+
? { templateArguments: classTemplateArguments }
|
|
2138
|
+
: {}),
|
|
2139
|
+
...(parsedTemplateConstraints !== undefined
|
|
2140
|
+
? { templateConstraints: parsedTemplateConstraints }
|
|
2141
|
+
: {}),
|
|
2142
|
+
...(frameworkHint
|
|
2143
|
+
? {
|
|
2144
|
+
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
2145
|
+
astFrameworkReason: frameworkHint.reason,
|
|
2146
|
+
}
|
|
2147
|
+
: {}),
|
|
2148
|
+
...(description !== undefined ? { description } : {}),
|
|
2149
|
+
...methodProps,
|
|
2150
|
+
...(declaredType !== undefined ? { declaredType } : {}),
|
|
2151
|
+
},
|
|
2152
|
+
});
|
|
2153
|
+
// enclosingClassId already computed above (before nodeId generation)
|
|
2154
|
+
const ownerId = enclosingClassId ?? objectLiteralOwnerInfo?.ownerId;
|
|
2155
|
+
result.symbols.push({
|
|
2156
|
+
filePath: file.path,
|
|
2157
|
+
name: nodeName,
|
|
2158
|
+
nodeId,
|
|
2159
|
+
type: nodeLabel,
|
|
2160
|
+
...(qualifiedTypeName !== undefined ? { qualifiedName: qualifiedTypeName } : {}),
|
|
2161
|
+
parameterCount: methodProps.parameterCount,
|
|
2162
|
+
requiredParameterCount: methodProps.requiredParameterCount,
|
|
2163
|
+
parameterTypes: methodProps.parameterTypes,
|
|
2164
|
+
parameterTypeClasses: methodProps.parameterTypeClasses,
|
|
2165
|
+
returnType: methodProps.returnType,
|
|
2166
|
+
...(declaredType !== undefined ? { declaredType } : {}),
|
|
2167
|
+
...(classTemplateArguments !== undefined && classTemplateArguments.length > 0
|
|
2168
|
+
? { templateArguments: classTemplateArguments }
|
|
2169
|
+
: {}),
|
|
2170
|
+
...(ownerId !== undefined ? { ownerId } : {}),
|
|
2171
|
+
visibility: methodProps.visibility,
|
|
2172
|
+
isStatic: methodProps.isStatic,
|
|
2173
|
+
isReadonly: methodProps.isReadonly,
|
|
2174
|
+
isAbstract: methodProps.isAbstract,
|
|
2175
|
+
isFinal: methodProps.isFinal,
|
|
2176
|
+
...(methodProps.isDeleted !== undefined
|
|
2177
|
+
? { isDeleted: methodProps.isDeleted }
|
|
2178
|
+
: {}),
|
|
2179
|
+
...(methodProps.isVirtual !== undefined
|
|
2180
|
+
? { isVirtual: methodProps.isVirtual }
|
|
2181
|
+
: {}),
|
|
2182
|
+
...(methodProps.isOverride !== undefined
|
|
2183
|
+
? { isOverride: methodProps.isOverride }
|
|
2184
|
+
: {}),
|
|
2185
|
+
...(methodProps.isAsync !== undefined ? { isAsync: methodProps.isAsync } : {}),
|
|
2186
|
+
...(methodProps.isPartial !== undefined
|
|
2187
|
+
? { isPartial: methodProps.isPartial }
|
|
2188
|
+
: {}),
|
|
2189
|
+
...(methodProps.annotations !== undefined
|
|
2190
|
+
? { annotations: methodProps.annotations }
|
|
2191
|
+
: {}),
|
|
2192
|
+
});
|
|
2193
|
+
// Only emit File -> Symbol DEFINES for top-level symbols (issue #1944).
|
|
2194
|
+
if (ownerId === undefined) {
|
|
2195
|
+
const fileId = (0, utils_js_1.generateId)('File', file.path);
|
|
2196
|
+
const relId = (0, utils_js_1.generateId)('DEFINES', `${fileId}->${nodeId}`);
|
|
2197
|
+
result.relationships.push({
|
|
2198
|
+
id: relId,
|
|
2199
|
+
sourceId: fileId,
|
|
2200
|
+
targetId: nodeId,
|
|
2201
|
+
type: 'DEFINES',
|
|
2202
|
+
confidence: 1.0,
|
|
2203
|
+
reason: '',
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
// ── HAS_METHOD / HAS_PROPERTY: link member to enclosing class ──
|
|
2207
|
+
if (ownerId !== undefined) {
|
|
2208
|
+
const memberEdgeType = nodeLabel === 'Property' ? 'HAS_PROPERTY' : 'HAS_METHOD';
|
|
2209
|
+
result.relationships.push({
|
|
2210
|
+
id: (0, utils_js_1.generateId)(memberEdgeType, `${ownerId}->${nodeId}`),
|
|
2211
|
+
sourceId: ownerId,
|
|
2212
|
+
targetId: nodeId,
|
|
2213
|
+
type: memberEdgeType,
|
|
2214
|
+
confidence: 1.0,
|
|
2215
|
+
reason: objectLiteralOwnerInfo
|
|
2216
|
+
? 'object literal method belongs to exported object binding'
|
|
2217
|
+
: '',
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
// ── Fetch wrapper detection: record functions that call fetch() internally ──
|
|
2221
|
+
if (nodeLabel === 'Function' &&
|
|
2222
|
+
definitionNode &&
|
|
2223
|
+
nameNode &&
|
|
2224
|
+
(language === index_js_1.SupportedLanguages.TypeScript || language === index_js_1.SupportedLanguages.JavaScript)) {
|
|
2225
|
+
if (checkForFetchCall(definitionNode)) {
|
|
2226
|
+
result.fetchWrapperDefs.push({
|
|
2227
|
+
filePath: file.path,
|
|
2228
|
+
functionName: nameNode.text,
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
// Extract framework routes via provider detection (e.g., Laravel routes.php)
|
|
2234
|
+
if (provider.isRouteFile?.(file.path)) {
|
|
2235
|
+
const extractedRoutes = (0, laravel_js_1.extractLaravelRoutes)(tree, file.path);
|
|
2236
|
+
for (const r of extractedRoutes)
|
|
2237
|
+
result.routes.push(r);
|
|
2238
|
+
}
|
|
2239
|
+
// Extract ORM queries (Prisma, Supabase)
|
|
2240
|
+
extractORMQueries(file.path, parseContent, result.ormQueries);
|
|
2241
|
+
// Extract FastAPI include_router(prefix=...) and `from <mod> import router`
|
|
2242
|
+
// sites. parse-impl aggregates these into a per-module prefix map and
|
|
2243
|
+
// injects the resolved prefix onto each ExtractedDecoratorRoute that
|
|
2244
|
+
// came from a `@router.<verb>` decorator. Python-only.
|
|
2245
|
+
if (language === index_js_1.SupportedLanguages.Python) {
|
|
2246
|
+
(0, fastapi_router_bindings_js_1.extractFastAPIRouterBindings)(file.path, parseContent, result.routerIncludes, result.routerImports, (result.routerModuleAliases ??= []), (result.routerConstructorPrefixes ??= []));
|
|
2247
|
+
// #2391: harvest module-level string constants + from-imports so parse-impl
|
|
2248
|
+
// can resolve non-literal decorator route paths cross-file. Only emit for
|
|
2249
|
+
// files that carry something resolvable (a constant definition or an import
|
|
2250
|
+
// binding) to keep the aggregate bounded on large repos.
|
|
2251
|
+
const constants = (0, python_const_resolver_js_1.extractPythonModuleConstants)(tree);
|
|
2252
|
+
if (constants.literals.size > 0 || constants.exprs.size > 0 || constants.imports.size > 0) {
|
|
2253
|
+
(result.moduleConstants ??= []).push({ filePath: file.path, constants });
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
// Language-specific decorator route extraction via provider hook.
|
|
2257
|
+
// The provider's extractDecoratorRoutes walks the AST for framework-specific
|
|
2258
|
+
// route patterns (e.g., Java Spring class-level prefix joining). Routes are
|
|
2259
|
+
// appended to decoratorRoutes for the routes phase to emit as Route nodes.
|
|
2260
|
+
if (provider.extractDecoratorRoutes) {
|
|
2261
|
+
const frameworkRoutes = provider.extractDecoratorRoutes(tree, file.path, lineOffset);
|
|
2262
|
+
for (const r of frameworkRoutes)
|
|
2263
|
+
result.decoratorRoutes.push(r);
|
|
2264
|
+
}
|
|
2265
|
+
// Project-wide route-inheritance type collection via provider hook (#2288).
|
|
2266
|
+
// The per-file SharedSpringType views are aggregated by the parse phase,
|
|
2267
|
+
// which then resolves interface-inherited routes cross-file.
|
|
2268
|
+
if (provider.extractRouteInheritanceTypes) {
|
|
2269
|
+
const springTypes = provider.extractRouteInheritanceTypes(tree, file.path);
|
|
2270
|
+
if (springTypes.length > 0)
|
|
2271
|
+
(result.springTypes ??= []).push(...springTypes);
|
|
2272
|
+
}
|
|
2273
|
+
// Vue: emit CALLS edges for components used in <template>
|
|
2274
|
+
if (language === index_js_1.SupportedLanguages.Vue) {
|
|
2275
|
+
const templateComponents = (0, vue_sfc_extractor_js_1.extractTemplateComponents)(file.content);
|
|
2276
|
+
for (const componentName of templateComponents) {
|
|
2277
|
+
result.calls.push({
|
|
2278
|
+
filePath: file.path,
|
|
2279
|
+
calledName: componentName,
|
|
2280
|
+
sourceId: (0, utils_js_1.generateId)('File', file.path),
|
|
2281
|
+
callForm: 'free',
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
// ============================================================================
|
|
2288
|
+
// Worker message handler — supports sub-batch streaming
|
|
2289
|
+
// ============================================================================
|
|
2290
|
+
/** Accumulated result across sub-batches */
|
|
2291
|
+
let accumulated = {
|
|
2292
|
+
nodes: [],
|
|
2293
|
+
relationships: [],
|
|
2294
|
+
symbols: [],
|
|
2295
|
+
calls: [],
|
|
2296
|
+
assignments: [],
|
|
2297
|
+
routes: [],
|
|
2298
|
+
fetchCalls: [],
|
|
2299
|
+
fetchWrapperDefs: [],
|
|
2300
|
+
decoratorRoutes: [],
|
|
2301
|
+
routerIncludes: [],
|
|
2302
|
+
routerImports: [],
|
|
2303
|
+
routerConstructorPrefixes: [],
|
|
2304
|
+
routerModuleAliases: [],
|
|
2305
|
+
toolDefs: [],
|
|
2306
|
+
ormQueries: [],
|
|
2307
|
+
constructorBindings: [],
|
|
2308
|
+
fileScopeBindings: [],
|
|
2309
|
+
parsedFiles: [],
|
|
2310
|
+
skippedLanguages: {},
|
|
2311
|
+
cfgSkipped: {},
|
|
2312
|
+
fileCount: 0,
|
|
2313
|
+
};
|
|
2314
|
+
let cumulativeProcessed = 0;
|
|
2315
|
+
// `mergeResult` (+ its `appendAll`) lives in ./result-merge.ts (extracted so it
|
|
2316
|
+
// can be unit-tested without importing this entry module).
|
|
2317
|
+
// Signal the pool that worker-side initialization (parser imports, language
|
|
2318
|
+
// grammars, type-env setup, all helper modules) is complete and the message
|
|
2319
|
+
// handler below is about to be attached. The pool's `waitForWorkerReady`
|
|
2320
|
+
// resolves on this handshake — without it, a worker that crashes during
|
|
2321
|
+
// top-of-script init slips past pool startup (Node's `online` event fires
|
|
2322
|
+
// before the script body runs) and the pool only notices via the first
|
|
2323
|
+
// dispatch's idle timeout (~30s). Emit once; the dispatch handler treats
|
|
2324
|
+
// any subsequent `ready` message as a benign no-op.
|
|
2325
|
+
//
|
|
2326
|
+
// Native postMessage carries the ready handshake — Node's structured
|
|
2327
|
+
// clone delivers `{type:'ready'}` to the pool's waitForWorkerReady
|
|
2328
|
+
// listener directly. The pool drops the slot if this isn't seen within
|
|
2329
|
+
// `WORKER_READY_TIMEOUT_MS` (5s), so emitting it AFTER all top-of-script
|
|
2330
|
+
// init (imports, native binding loads, type-env setup) completes is the
|
|
2331
|
+
// load-bearing signal that this worker is ready for dispatch.
|
|
2332
|
+
bootstrapLog('ready sent');
|
|
2333
|
+
node_worker_threads_1.parentPort.postMessage({ type: 'ready' });
|
|
2334
|
+
// Module-scope `TextDecoder` for sub-batch content. The pool sends each
|
|
2335
|
+
// file's content as a `Uint8Array` (zero-copy ArrayBuffer transfer); we
|
|
2336
|
+
// decode to string lazily here, once per file, before handing to
|
|
2337
|
+
// tree-sitter. Hoisted to module scope so we don't allocate a new
|
|
2338
|
+
// ICU-backed decoder per sub-batch — `TextDecoder.decode()` is
|
|
2339
|
+
// stateless across calls and safe to share.
|
|
2340
|
+
const sharedContentDecoder = new TextDecoder('utf-8');
|
|
2341
|
+
/**
|
|
2342
|
+
* Convert the pool's sub-batch `files` array (content as `Uint8Array`,
|
|
2343
|
+
* transferred zero-copy) into the `ParseWorkerInput[]` shape
|
|
2344
|
+
* `processBatch` expects (content as `string`). This is the one place
|
|
2345
|
+
* the UTF-8 decode happens — runs on the worker thread in parallel with
|
|
2346
|
+
* continued main-thread work.
|
|
2347
|
+
*/
|
|
2348
|
+
function decodeSubBatchFiles(files) {
|
|
2349
|
+
return files.map((f) => ({
|
|
2350
|
+
path: f.path,
|
|
2351
|
+
// Test scaffolding (the writeReadyWorker preamble that wraps
|
|
2352
|
+
// parentPort.on) may already convert content to string before
|
|
2353
|
+
// calling here; tolerate both shapes so the same worker code
|
|
2354
|
+
// exercises real and synthetic dispatches.
|
|
2355
|
+
content: typeof f.content === 'string' ? f.content : sharedContentDecoder.decode(f.content),
|
|
2356
|
+
}));
|
|
2357
|
+
}
|
|
2358
|
+
let firstTaskLogged = false;
|
|
2359
|
+
node_worker_threads_1.parentPort.on('message', (msg) => {
|
|
2360
|
+
if (!firstTaskLogged) {
|
|
2361
|
+
firstTaskLogged = true;
|
|
2362
|
+
bootstrapLog('first task received');
|
|
2363
|
+
}
|
|
2364
|
+
try {
|
|
2365
|
+
// Sub-batch mode: { type: 'sub-batch', files: [...] }
|
|
2366
|
+
if (msg.type === 'sub-batch') {
|
|
2367
|
+
const files = decodeSubBatchFiles(msg.files);
|
|
2368
|
+
const result = processBatch(files, (filesProcessed) => {
|
|
2369
|
+
node_worker_threads_1.parentPort.postMessage({
|
|
2370
|
+
type: 'progress',
|
|
2371
|
+
filesProcessed: cumulativeProcessed + filesProcessed,
|
|
2372
|
+
});
|
|
2373
|
+
});
|
|
2374
|
+
cumulativeProcessed += result.fileCount;
|
|
2375
|
+
(0, result_merge_js_1.mergeResult)(accumulated, result);
|
|
2376
|
+
// Signal ready for next sub-batch
|
|
2377
|
+
node_worker_threads_1.parentPort.postMessage({ type: 'sub-batch-done' });
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
// Flush: send accumulated results
|
|
2381
|
+
if (msg.type === 'flush') {
|
|
2382
|
+
// #1983 parallel serialization: when a store path is configured, write
|
|
2383
|
+
// this job's ParsedFiles to our own disk shard HERE (at the flush
|
|
2384
|
+
// boundary, where `accumulated.parsedFiles` is complete) and drop them
|
|
2385
|
+
// from the result so the main thread never deserializes/re-serializes
|
|
2386
|
+
// them. Writing at flush — not per sub-batch — encodes the invariant
|
|
2387
|
+
// "a shard is written iff its result is delivered": a worker that dies
|
|
2388
|
+
// before flush wrote no shard, so the pool's job retry yields exactly
|
|
2389
|
+
// one. `undefined` store path keeps ParsedFiles in the result (no-store
|
|
2390
|
+
// fallback). The write is synchronous: blocking this dedicated worker
|
|
2391
|
+
// thread protects the main thread and avoids threading async through the
|
|
2392
|
+
// accumulate path; per-job write time is small vs the parse it follows.
|
|
2393
|
+
if ((PARSED_FILE_STORE_STORAGE_PATH || DURABLE_PARSED_FILE_STORAGE_PATH) &&
|
|
2394
|
+
accumulated.parsedFiles.length > 0) {
|
|
2395
|
+
const seq = shardSeq++;
|
|
2396
|
+
// #2038 warm-cache coverage: ALSO write a durable, content-addressed
|
|
2397
|
+
// shard keyed by chunk hash so a future warm parse-cache hit (no worker
|
|
2398
|
+
// runs) can restore these ParsedFiles without re-parsing. Same bytes,
|
|
2399
|
+
// same `seq`, so durable and run-scoped shards correlate. Only when the
|
|
2400
|
+
// flush carried a chunk hash (content-addressed dispatch).
|
|
2401
|
+
if (DURABLE_PARSED_FILE_STORAGE_PATH && typeof msg.chunkHash === 'string') {
|
|
2402
|
+
(0, parsedfile_store_js_1.persistDurableParsedFileShardSync)(DURABLE_PARSED_FILE_STORAGE_PATH, msg.chunkHash, node_worker_threads_1.threadId, seq, accumulated.parsedFiles);
|
|
2403
|
+
}
|
|
2404
|
+
if (PARSED_FILE_STORE_STORAGE_PATH) {
|
|
2405
|
+
(0, parsedfile_store_js_1.persistParsedFileShardSync)(PARSED_FILE_STORE_STORAGE_PATH, `w${node_worker_threads_1.threadId}-${seq}`, accumulated.parsedFiles);
|
|
2406
|
+
accumulated.parsedFiles = [];
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
(0, post_result_js_1.postResultCloneSafe)(accumulated);
|
|
2410
|
+
// Reset for potential reuse
|
|
2411
|
+
accumulated = {
|
|
2412
|
+
nodes: [],
|
|
2413
|
+
relationships: [],
|
|
2414
|
+
symbols: [],
|
|
2415
|
+
calls: [],
|
|
2416
|
+
assignments: [],
|
|
2417
|
+
routes: [],
|
|
2418
|
+
fetchCalls: [],
|
|
2419
|
+
fetchWrapperDefs: [],
|
|
2420
|
+
decoratorRoutes: [],
|
|
2421
|
+
routerIncludes: [],
|
|
2422
|
+
routerImports: [],
|
|
2423
|
+
routerConstructorPrefixes: [],
|
|
2424
|
+
routerModuleAliases: [],
|
|
2425
|
+
toolDefs: [],
|
|
2426
|
+
ormQueries: [],
|
|
2427
|
+
constructorBindings: [],
|
|
2428
|
+
fileScopeBindings: [],
|
|
2429
|
+
parsedFiles: [],
|
|
2430
|
+
skippedLanguages: {},
|
|
2431
|
+
cfgSkipped: {},
|
|
2432
|
+
fileCount: 0,
|
|
2433
|
+
};
|
|
2434
|
+
cumulativeProcessed = 0;
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
catch (err) {
|
|
2439
|
+
// Carry the worker-side stack across the MessageChannel, not just the
|
|
2440
|
+
// message. Without this, an unexpected worker throw (e.g. the minified
|
|
2441
|
+
// `this.#<x> is not a function` family) reaches the operator as a bare
|
|
2442
|
+
// one-liner with no file:line — exactly what made #2068 undebuggable. The
|
|
2443
|
+
// pool embeds `errorStack` into its death/circuit-breaker reason so the
|
|
2444
|
+
// surfaced "Phase 'parse' failed" message points at the real frame (the
|
|
2445
|
+
// stack's first line already carries the error's type + message). We send
|
|
2446
|
+
// primitive fields (not the raw Error) so a non-cloneable `cause` payload
|
|
2447
|
+
// can never turn the report itself into a `messageerror`. `errorStack` is
|
|
2448
|
+
// optional on the wire, so an older pool ignores it.
|
|
2449
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
2450
|
+
node_worker_threads_1.parentPort.postMessage({
|
|
2451
|
+
type: 'error',
|
|
2452
|
+
error: e.message,
|
|
2453
|
+
errorStack: e.stack,
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
});
|