cgraphx 2.0.1 → 2.0.3
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 +7 -5
- package/dist/.claude-template/commands/end.md +1 -1
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +2 -1
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +4 -2
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +5 -2
- package/dist/.claude-template/skills/iboc-check/SKILL.md +116 -0
- package/dist/.claude-template/skills/iboc-check/filler-prompt.md +75 -0
- package/dist/.claude-template/skills/implementation/SKILL.md +7 -0
- package/dist/.claude-template/skills/init-project-guides/SKILL.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
- package/dist/.claude-template/skills/write-api/assets/template-request.bru +3 -3
- package/dist/.claude-template/skills/write-api/references/bru-format.md +6 -6
- package/dist/.claude-template/skills/write-delivery-guide/SKILL.md +97 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-change-type.md +120 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-multi-role-flow.md +148 -0
- package/dist/.claude-template/skills/write-unit-test-code/SKILL.md +23 -6
- 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/features/scanner.d.ts +2 -0
- package/dist/core/features/scanner.d.ts.map +1 -1
- package/dist/core/features/scanner.js +27 -6
- package/dist/core/features/scanner.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/package.json +1 -1
|
@@ -0,0 +1,1286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parse implementation — chunked parse + resolve loop.
|
|
4
|
+
*
|
|
5
|
+
* This is the core parsing engine of the ingestion pipeline. It reads
|
|
6
|
+
* source files in byte-budget chunks (~20MB each), parses via the worker
|
|
7
|
+
* pool (the sole parse path — there is no sequential fallback), and emits
|
|
8
|
+
* route CALLS edges. Import,
|
|
9
|
+
* call, and inheritance resolution are owned by the scope-resolution
|
|
10
|
+
* phase, not here (RING4-1 #942 removed the legacy call DAG; RING4-2 #943
|
|
11
|
+
* removed the legacy per-file import resolution + wildcard synthesis).
|
|
12
|
+
*
|
|
13
|
+
* Consumed by the parse phase (`parse.ts`) — the phase file handles
|
|
14
|
+
* dependency wiring while the heavy implementation lives here.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.projectParseHeapNeedBytes = projectParseHeapNeedBytes;
|
|
23
|
+
exports.shouldAbortForHeapPressure = shouldAbortForHeapPressure;
|
|
24
|
+
exports.heapPressureRemedy = heapPressureRemedy;
|
|
25
|
+
exports.extractCrossFileRoutes = extractCrossFileRoutes;
|
|
26
|
+
exports.handleWorkerStartupFailure = handleWorkerStartupFailure;
|
|
27
|
+
exports.runChunkedParseAndResolve = runChunkedParseAndResolve;
|
|
28
|
+
const binding_accumulator_js_1 = require("../binding-accumulator.js");
|
|
29
|
+
const parsing_processor_js_1 = require("../parsing-processor.js");
|
|
30
|
+
const parse_cache_js_1 = require("../../../storage/parse-cache.js");
|
|
31
|
+
const parsedfile_store_js_1 = require("../../../storage/parsedfile-store.js");
|
|
32
|
+
const call_processor_js_1 = require("../call-processor.js");
|
|
33
|
+
const index_js_1 = require("../model/index.js");
|
|
34
|
+
const index_js_2 = require("../../../../shared/index.js");
|
|
35
|
+
const filesystem_walker_js_1 = require("../filesystem-walker.js");
|
|
36
|
+
const parser_loader_js_1 = require("../../tree-sitter/parser-loader.js");
|
|
37
|
+
const safe_parse_js_1 = require("../../tree-sitter/safe-parse.js");
|
|
38
|
+
const index_js_3 = require("../languages/index.js");
|
|
39
|
+
const worker_pool_js_1 = require("../workers/worker-pool.js");
|
|
40
|
+
const route_path_js_1 = require("../route-extractors/route-path.js");
|
|
41
|
+
const python_const_resolver_js_1 = require("../route-extractors/python-const-resolver.js");
|
|
42
|
+
const spring_shared_js_1 = require("../route-extractors/spring-shared.js");
|
|
43
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
44
|
+
const effective_ram_js_1 = require("../utils/effective-ram.js");
|
|
45
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
46
|
+
const node_v8_1 = __importDefault(require("node:v8"));
|
|
47
|
+
const node_url_1 = require("node:url");
|
|
48
|
+
const env_js_1 = require("../utils/env.js");
|
|
49
|
+
const verbose_js_1 = require("../utils/verbose.js");
|
|
50
|
+
const deferred_resolution_profile_js_1 = require("../utils/deferred-resolution-profile.js");
|
|
51
|
+
const heap_probe_js_1 = require("../utils/heap-probe.js");
|
|
52
|
+
const logger_js_1 = require("../../logger.js");
|
|
53
|
+
// ── Constants ──────────────────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Heap-scale guardrail constants (#2649). Measured on a Linux-kernel analyze:
|
|
56
|
+
* ~75 graph nodes per PARSEABLE file (~5M nodes / ~65k parseable files;
|
|
57
|
+
* validated against heap probes at chunks 25/50/75 of 113 — the first
|
|
58
|
+
* calibration divided by total scanned files and under-projected by ~30%),
|
|
59
|
+
* main-thread heap per node. C-heavy corpus; other language mixes vary — these
|
|
60
|
+
* feed a WARNING and an emergency abort, never a hard admission gate, so
|
|
61
|
+
* estimate error only shifts when the operator hears about the problem, not
|
|
62
|
+
* whether analyze runs.
|
|
63
|
+
*
|
|
64
|
+
* RECALIBRATED for streamed structural emit (#2680), which is on by default for
|
|
65
|
+
* full rebuilds and holds relationships out of the JS heap. The original 2250
|
|
66
|
+
* was measured against the object-based graph; an A/B at 400k nodes / 1.08M
|
|
67
|
+
* edges put streaming at 1.40x smaller (819 MB -> 584 MB), so the corpus-
|
|
68
|
+
* calibrated figure is divided by that ratio: 2250 / 1.40 ~= 1600. Scaling the
|
|
69
|
+
* measured constant rather than substituting a synthetic one keeps #2649's
|
|
70
|
+
* kernel calibration intact and changes only the one thing that actually moved.
|
|
71
|
+
*
|
|
72
|
+
* If streaming is disabled (CGRAPH_STREAM_GRAPH_EMIT=0, or any non-force run)
|
|
73
|
+
* this UNDER-projects by ~40%, so the preflight warning may stay quiet on a repo
|
|
74
|
+
* that then struggles. That is the safe direction to be wrong in: the abort
|
|
75
|
+
* below reads LIVE heap use, not this projection, so it still catches the real
|
|
76
|
+
* condition — only the early warning is affected.
|
|
77
|
+
*/
|
|
78
|
+
const PROJECTED_NODES_PER_FILE = 75;
|
|
79
|
+
const PROJECTED_HEAP_BYTES_PER_NODE = 1600;
|
|
80
|
+
/** Warn at scan end when the projection crosses this share of the heap limit. */
|
|
81
|
+
const PREFLIGHT_WARN_FRACTION = 0.85;
|
|
82
|
+
/**
|
|
83
|
+
* Abort the chunk loop when live heap use crosses this share of the limit.
|
|
84
|
+
* Above ~0.95 V8 enters the ineffective-mark-compact death spiral (2s+ GC
|
|
85
|
+
* pauses that also falsely idle-timeout healthy workers, #2649); 0.92 leaves
|
|
86
|
+
* one chunk's worth of headroom to fail with an actionable message instead.
|
|
87
|
+
* `CGRAPH_MEMORY=off` declines the abort (proceed-at-own-risk).
|
|
88
|
+
*/
|
|
89
|
+
const HEAP_ABORT_FRACTION = 0.92;
|
|
90
|
+
/** Projected main-thread heap need for the parse phase (#2649). */
|
|
91
|
+
function projectParseHeapNeedBytes(parseableFileCount) {
|
|
92
|
+
return parseableFileCount * PROJECTED_NODES_PER_FILE * PROJECTED_HEAP_BYTES_PER_NODE;
|
|
93
|
+
}
|
|
94
|
+
/** True when the mid-loop heap guard should abort the parse (#2649). */
|
|
95
|
+
function shouldAbortForHeapPressure(heapUsedBytes, heapLimitBytes) {
|
|
96
|
+
if ((0, effective_ram_js_1.memoryAutopilotDisabled)())
|
|
97
|
+
return false;
|
|
98
|
+
return heapUsedBytes > heapLimitBytes * HEAP_ABORT_FRACTION;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The ONE action a user should take when this repository doesn't fit the
|
|
102
|
+
* current heap (#2649). Users hitting memory limits are already frustrated —
|
|
103
|
+
* a menu of env knobs at that moment is noise. Branch on whether the machine
|
|
104
|
+
* itself has more memory to give: if this process's limit sits well below
|
|
105
|
+
* what the RAM-aware auto-sizer would grant (an inherited NODE_OPTIONS pin or
|
|
106
|
+
* explicit flag), the fix is to drop the pin — gitnexus sizes itself.
|
|
107
|
+
* Otherwise the machine is the ceiling and only scope or hardware helps.
|
|
108
|
+
* Escape hatches (CGRAPH_MEMORY etc.) stay in the README env table.
|
|
109
|
+
*/
|
|
110
|
+
function heapPressureRemedy(heapLimitBytes) {
|
|
111
|
+
// Effective RAM honors a real cgroup limit — raw os.totalmem() told users
|
|
112
|
+
// inside an 8GB-limited container on a 64GB host that "this machine has
|
|
113
|
+
// more memory available", an advice loop with no exit (#2649 review).
|
|
114
|
+
const autoCapBytes = (0, effective_ram_js_1.effectiveRamBytes)() * 0.75;
|
|
115
|
+
if (heapLimitBytes < autoCapBytes * 0.9) {
|
|
116
|
+
return (`This machine has more memory available: re-run without the --max-old-space-size ` +
|
|
117
|
+
`pin (NODE_OPTIONS or node flag) — gitnexus sizes its heap to the machine automatically.`);
|
|
118
|
+
}
|
|
119
|
+
return (`This machine is at its memory ceiling: exclude generated or vendored directories ` +
|
|
120
|
+
`via .cgraphignore, or analyze on a machine with more memory.`);
|
|
121
|
+
}
|
|
122
|
+
/** Max bytes of source content to load per parse chunk.
|
|
123
|
+
*
|
|
124
|
+
* Memory bound for the worker pool dispatch + a granularity knob for
|
|
125
|
+
* the parse cache. A single file change invalidates only its enclosing
|
|
126
|
+
* chunk, so smaller budgets → finer-grained invalidation.
|
|
127
|
+
*
|
|
128
|
+
* Override via CGRAPH_CHUNK_BYTE_BUDGET (bytes) — the default of 2MB
|
|
129
|
+
* gives a useful invalidation floor (~1/N chunks on a multi-MB repo)
|
|
130
|
+
* while keeping worker dispatch overhead under 5% on cold runs.
|
|
131
|
+
*/
|
|
132
|
+
/**
|
|
133
|
+
* Built-in chunk byte budget when neither `PipelineOptions.chunkByteBudget`
|
|
134
|
+
* nor `CGRAPH_CHUNK_BYTE_BUDGET` is set. Tuned to give a useful
|
|
135
|
+
* cache-invalidation floor (~1/N chunks on a multi-MB repo) while keeping
|
|
136
|
+
* worker dispatch overhead under 5% on cold runs. Resolution happens at
|
|
137
|
+
* call time inside `runChunkedParseAndResolve` (U14 from PR #1693 review)
|
|
138
|
+
* — previously this was a module-load IIFE, which froze the env value at
|
|
139
|
+
* import time and meant per-call option threading silently no-op'd.
|
|
140
|
+
*/
|
|
141
|
+
const DEFAULT_CHUNK_BYTE_BUDGET = 2 * 1024 * 1024;
|
|
142
|
+
/**
|
|
143
|
+
* Per-worker share of a chunk's byte budget when auto-scaling (#worker-idle).
|
|
144
|
+
*
|
|
145
|
+
* A chunk is a single `WorkerPool.dispatch` unit; the pool fans a chunk's files
|
|
146
|
+
* into sub-batch jobs and assigns them to idle workers (`wakeIdleSlots`). When
|
|
147
|
+
* the chunk budget (2 MB) was far below the 8 MB sub-batch cap, every chunk
|
|
148
|
+
* produced exactly ONE job → ONE busy worker while the other N-1 sat idle. To
|
|
149
|
+
* keep all workers fed, the auto chunk budget now scales as
|
|
150
|
+
* `poolSize × CHUNK_BYTES_PER_WORKER`, so each dispatch carries enough work to
|
|
151
|
+
* fan across the whole pool. Sequential / explicit-budget runs are unaffected.
|
|
152
|
+
*/
|
|
153
|
+
const CHUNK_BYTES_PER_WORKER = 2 * 1024 * 1024;
|
|
154
|
+
/**
|
|
155
|
+
* Target jobs-per-worker per dispatch. More jobs than workers gives the pool's
|
|
156
|
+
* idle-slot assignment room to load-balance (a slow job doesn't strand a worker
|
|
157
|
+
* while the rest finish early). Drives the derived `subBatchMaxBytes`.
|
|
158
|
+
*/
|
|
159
|
+
const TARGET_JOBS_PER_WORKER = 3;
|
|
160
|
+
/** Floor for a derived sub-batch so jobs don't shrink to per-file IPC churn. */
|
|
161
|
+
const MIN_SUB_BATCH_BYTES = 256 * 1024;
|
|
162
|
+
function resolveChunkByteBudget(options, effectivePoolSize = 1) {
|
|
163
|
+
const opt = options?.chunkByteBudget;
|
|
164
|
+
if (typeof opt === 'number' && Number.isFinite(opt) && opt > 0)
|
|
165
|
+
return opt;
|
|
166
|
+
const env = Number(process.env.CGRAPH_CHUNK_BYTE_BUDGET);
|
|
167
|
+
if (Number.isFinite(env) && env > 0)
|
|
168
|
+
return env;
|
|
169
|
+
// Auto: size each chunk so a dispatch can fan across the whole pool. A
|
|
170
|
+
// single-worker (tiny-repo) run keeps the original 2 MB invalidation floor.
|
|
171
|
+
return Math.max(DEFAULT_CHUNK_BYTE_BUDGET, effectivePoolSize * CHUNK_BYTES_PER_WORKER);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Whole-repo, cross-file route extraction (main thread).
|
|
175
|
+
*
|
|
176
|
+
* Some frameworks define their route table from a single root file that pulls
|
|
177
|
+
* in other files across the repo — e.g. Django follows
|
|
178
|
+
* `manage.py → DJANGO_SETTINGS_MODULE → ROOT_URLCONF → root urls.py`, then walks
|
|
179
|
+
* `include()` chains across many files. Unlike single-file route files (Laravel
|
|
180
|
+
* `routes/*.php`), which the parse worker extracts in isolation, these need a
|
|
181
|
+
* whole-repo view and on-demand cross-file reads — neither of which the
|
|
182
|
+
* filesystem-free worker can provide, and which a per-chunk worker view gets
|
|
183
|
+
* wrong whenever the root file and its includes land in different chunks.
|
|
184
|
+
*
|
|
185
|
+
* So it runs here, once, after every file is scanned — mirroring the FastAPI
|
|
186
|
+
* router-include join further below. The pass is language-agnostic: any
|
|
187
|
+
* {@link LanguageProvider} exposing both `discoverRootRouteFile` and
|
|
188
|
+
* `extractRoutes` participates (today only Python/Django). For repos without
|
|
189
|
+
* such a framework the cost is a path scan plus one `manage.py`-style miss.
|
|
190
|
+
*/
|
|
191
|
+
async function extractCrossFileRoutes(allPaths, repoPath) {
|
|
192
|
+
const out = [];
|
|
193
|
+
// Languages whose provider implements the cross-file route hooks. Route
|
|
194
|
+
// results are intentionally NOT persisted across analyze runs, so a repo
|
|
195
|
+
// using such a framework (e.g. Django) re-derives its routes on every run;
|
|
196
|
+
// a repo without one does effectively nothing here. Cross-run route caching
|
|
197
|
+
// is a deliberate follow-up — see #1836.
|
|
198
|
+
const routeCapableLangs = new Set();
|
|
199
|
+
for (const provider of Object.values(index_js_3.providers)) {
|
|
200
|
+
if (provider.discoverRootRouteFiles && provider.extractRoutes) {
|
|
201
|
+
routeCapableLangs.add(provider.id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (routeCapableLangs.size === 0)
|
|
205
|
+
return out;
|
|
206
|
+
// Bucket only the paths whose language can contribute routes, so a non-
|
|
207
|
+
// framework repo never pays to bucket the languages it doesn't use here.
|
|
208
|
+
const pathsByLang = new Map();
|
|
209
|
+
for (const p of allPaths) {
|
|
210
|
+
const lang = (0, index_js_2.getLanguageFromFilename)(p);
|
|
211
|
+
if (!lang || !routeCapableLangs.has(lang))
|
|
212
|
+
continue;
|
|
213
|
+
let bucket = pathsByLang.get(lang);
|
|
214
|
+
if (!bucket) {
|
|
215
|
+
bucket = [];
|
|
216
|
+
pathsByLang.set(lang, bucket);
|
|
217
|
+
}
|
|
218
|
+
bucket.push(p);
|
|
219
|
+
}
|
|
220
|
+
for (const [lang, langPaths] of pathsByLang) {
|
|
221
|
+
if (!(0, parser_loader_js_1.isLanguageAvailable)(lang))
|
|
222
|
+
continue;
|
|
223
|
+
const provider = (0, index_js_3.getProvider)(lang);
|
|
224
|
+
if (!provider.discoverRootRouteFiles || !provider.extractRoutes)
|
|
225
|
+
continue;
|
|
226
|
+
// Disk-backed reader keyed on repo-relative paths. Discovery and the
|
|
227
|
+
// include() walk read through this; nothing is pre-loaded, so a repo that
|
|
228
|
+
// lacks the framework pays only the reads its own discovery probes trigger.
|
|
229
|
+
const readCache = new Map();
|
|
230
|
+
const reader = (relativePath) => {
|
|
231
|
+
const cached = readCache.get(relativePath);
|
|
232
|
+
if (cached !== undefined)
|
|
233
|
+
return cached;
|
|
234
|
+
let content = null;
|
|
235
|
+
try {
|
|
236
|
+
content = node_fs_1.default.readFileSync(node_path_1.default.join(repoPath, relativePath), 'utf-8');
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
content = null;
|
|
240
|
+
}
|
|
241
|
+
readCache.set(relativePath, content);
|
|
242
|
+
return content;
|
|
243
|
+
};
|
|
244
|
+
// One root route file per discoverable project (a monorepo can have several).
|
|
245
|
+
const rootPaths = provider.discoverRootRouteFiles(langPaths.map((p) => ({ path: p })), undefined, reader);
|
|
246
|
+
if (rootPaths.length === 0)
|
|
247
|
+
continue;
|
|
248
|
+
// One parser per language — the grammar is language-scoped, so it is reused
|
|
249
|
+
// for every project root and every include() re-parse.
|
|
250
|
+
let parser;
|
|
251
|
+
try {
|
|
252
|
+
parser = await (0, parser_loader_js_1.createParserForLanguage)(lang, rootPaths[0]);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
continue; // grammar unavailable — skip the language, mirrors worker safety net
|
|
256
|
+
}
|
|
257
|
+
for (const rootPath of rootPaths) {
|
|
258
|
+
const rootContent = reader(rootPath);
|
|
259
|
+
if (rootContent === null)
|
|
260
|
+
continue; // skip this root only, not the language
|
|
261
|
+
let rootTree;
|
|
262
|
+
try {
|
|
263
|
+
rootTree = (0, safe_parse_js_1.parseSourceSafe)(parser, rootContent);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
logger_js_1.logger.warn(`Skipping unparseable root route file: ${rootPath}`);
|
|
267
|
+
continue; // skip this root only
|
|
268
|
+
}
|
|
269
|
+
// Isolate a misbehaving provider: a throw here must not abort the whole
|
|
270
|
+
// analyze (mirrors the worker's per-file isolation). Skip this root, warn.
|
|
271
|
+
try {
|
|
272
|
+
const routes = provider.extractRoutes(rootTree, rootPath, reader, parser);
|
|
273
|
+
for (const r of routes)
|
|
274
|
+
out.push(r);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
logger_js_1.logger.warn({ err }, `Cross-file route extraction failed for ${rootPath}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Handle a worker-pool startup failure by FAILING FAST with the captured cause
|
|
285
|
+
* (#1741). The pool self-heals *transient* worker crashes on its own — a
|
|
286
|
+
* bounded, jittered startup restart loop (see worker-pool.ts) — so this is
|
|
287
|
+
* reached only when that self-heal is EXHAUSTED, or a deterministic crash-loop
|
|
288
|
+
* was detected, or the pool could not even be constructed. In every such case
|
|
289
|
+
* the workers genuinely cannot start.
|
|
290
|
+
*
|
|
291
|
+
* There is no sequential parser to silently degrade to — that fallback was
|
|
292
|
+
* removed (and it had masked a worker-startup regression as a 2-hour "stuck"
|
|
293
|
+
* run in #1741, rc99: a dropped `logger.warn` plus an unbounded sequential
|
|
294
|
+
* grind). GitNexus surfaces the real crash and aborts so the operator fixes the
|
|
295
|
+
* worker startup (commonly a missing build). The pool's own crash
|
|
296
|
+
* classification (`crashClass` on WorkerPoolInitializationError) sharpens the
|
|
297
|
+
* message.
|
|
298
|
+
*
|
|
299
|
+
* @throws always — an actionable Error carrying the captured worker crash.
|
|
300
|
+
* @internal Exported for unit tests; production callers are the parse loop's
|
|
301
|
+
* two worker-startup catch sites below.
|
|
302
|
+
*/
|
|
303
|
+
function handleWorkerStartupFailure(err) {
|
|
304
|
+
const isInit = err instanceof worker_pool_js_1.WorkerPoolInitializationError;
|
|
305
|
+
const readinessFailures = isInit ? err.readinessFailures : [];
|
|
306
|
+
const crashClass = isInit ? err.crashClass : undefined;
|
|
307
|
+
// Surface the real cause verbatim: readiness failures for an init crash, or
|
|
308
|
+
// the construction error message (e.g. "Worker script not found: …") when the
|
|
309
|
+
// pool never got to spawn workers.
|
|
310
|
+
const failureDetail = readinessFailures.length > 0
|
|
311
|
+
? ` Underlying worker failure(s): ${readinessFailures.join(' | ')}`
|
|
312
|
+
: isInit
|
|
313
|
+
? ''
|
|
314
|
+
: ` Underlying error: ${err.message}`;
|
|
315
|
+
// Always surface the real crash — never let a startup failure pass silently.
|
|
316
|
+
logger_js_1.logger.error({ err: err.message, readinessFailures, crashClass }, 'Worker pool failed to start — workers could not start (bounded self-heal exhausted).');
|
|
317
|
+
const cause = crashClass === 'deterministic-startup'
|
|
318
|
+
? `every worker crashed identically during startup (a deterministic ` +
|
|
319
|
+
`crash-loop — retrying cannot help), so the pool has no usable workers.`
|
|
320
|
+
: isInit
|
|
321
|
+
? `workers exhausted the bounded startup retry budget without reporting ` +
|
|
322
|
+
`ready, so the pool has no usable workers.`
|
|
323
|
+
: `the worker pool could not be constructed.`;
|
|
324
|
+
// Class-aware fix hint: a missing/broken native binding is the likely cause
|
|
325
|
+
// when workers crashed during init, but it is the WRONG guess for a pool that
|
|
326
|
+
// never constructed (commonly a missing build / unresolvable worker path).
|
|
327
|
+
const fixHint = isInit
|
|
328
|
+
? `Fix the worker startup failure shown above (often a missing/broken native ` +
|
|
329
|
+
`binding or a top-of-script import error in parse-worker).`
|
|
330
|
+
: `Fix the worker pool construction error shown above (commonly a missing ` +
|
|
331
|
+
`build, so dist/ has no parse-worker, or an unresolvable worker path).`;
|
|
332
|
+
throw new Error(`Worker pool failed to start: ${cause}${failureDetail}\n\n` +
|
|
333
|
+
`The worker pool is GitNexus's only parse path — there is no sequential ` +
|
|
334
|
+
`fallback to hide this crash behind (silently degrading masked a ` +
|
|
335
|
+
`worker-startup regression as a 2-hour "stuck" run in #1741). Fix:\n` +
|
|
336
|
+
` • ${fixHint}`);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Chunked parse + resolve loop.
|
|
340
|
+
*
|
|
341
|
+
* Reads source in byte-budget chunks (~20MB each):
|
|
342
|
+
* 1. Parse each chunk via the worker pool (the sole parse path)
|
|
343
|
+
* 2. After all chunks parse, emit route CALLS edges (deferred so resolution
|
|
344
|
+
* sees the full repo graph) and collect the exported-type map
|
|
345
|
+
* 3. Collect TypeEnv bindings for cross-file propagation
|
|
346
|
+
*
|
|
347
|
+
* Import, call, and inheritance edges are emitted by the scope-resolution
|
|
348
|
+
* phase, not here (RING4-1 #942 / RING4-2 #943 removed the legacy passes).
|
|
349
|
+
*/
|
|
350
|
+
async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress, options) {
|
|
351
|
+
const model = (0, index_js_1.createSemanticModel)();
|
|
352
|
+
const symbolTable = model.symbols;
|
|
353
|
+
const parseableScanned = scannedFiles.filter((f) => {
|
|
354
|
+
const lang = (0, index_js_2.getLanguageFromFilename)(f.path);
|
|
355
|
+
return lang && (0, parser_loader_js_1.isLanguageAvailable)(lang);
|
|
356
|
+
});
|
|
357
|
+
// Warn about files skipped due to unavailable parsers
|
|
358
|
+
const skippedByLang = new Map();
|
|
359
|
+
for (const f of scannedFiles) {
|
|
360
|
+
const lang = (0, index_js_2.getLanguageFromFilename)(f.path);
|
|
361
|
+
const provider = lang === null ? undefined : (0, index_js_3.getProvider)(lang);
|
|
362
|
+
if (lang && provider?.parseStrategy !== 'standalone' && !(0, parser_loader_js_1.isLanguageAvailable)(lang)) {
|
|
363
|
+
skippedByLang.set(lang, (skippedByLang.get(lang) || 0) + 1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
for (const [lang, count] of skippedByLang) {
|
|
367
|
+
// Distinguish a deliberate runtime opt-out from a genuinely-missing binding
|
|
368
|
+
// so we don't tell a user who set CGRAPH_SKIP_OPTIONAL_GRAMMARS to
|
|
369
|
+
// `npm rebuild` a grammar that built fine (#2091/#2093 review).
|
|
370
|
+
if ((0, parser_loader_js_1.isGrammarRuntimeSkipped)(lang)) {
|
|
371
|
+
logger_js_1.logger.warn(`Skipping ${count} ${lang} file(s) — ${lang} parsing disabled via CGRAPH_SKIP_OPTIONAL_GRAMMARS.`);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
logger_js_1.logger.warn(`Skipping ${count} ${lang} file(s) — ${lang} parser not available (native binding may not have built). Try: npm rebuild tree-sitter-${lang}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// Sort parseableScanned alphabetically for stable chunk membership
|
|
378
|
+
// across runs (Finding 4). Without this, filesystem-scan order can
|
|
379
|
+
// shift between runs (notably on macOS APFS where directory entry
|
|
380
|
+
// order can change after modifications) — different files in the
|
|
381
|
+
// same chunk → different chunk hash → cache miss even when no file
|
|
382
|
+
// content changed. The cache also becomes platform-specific: a
|
|
383
|
+
// Linux-built cache misses on macOS for the same repo.
|
|
384
|
+
parseableScanned.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
385
|
+
const totalParseable = parseableScanned.length;
|
|
386
|
+
const totalBytes = parseableScanned.reduce((sum, f) => sum + f.size, 0);
|
|
387
|
+
if (totalParseable === 0) {
|
|
388
|
+
onProgress({
|
|
389
|
+
phase: 'parsing',
|
|
390
|
+
// Skip directly to the end of the parse-phase progress band (M2 from PR
|
|
391
|
+
// #1693 review). Parse 20-70%, deferred 70-95%; nothing in either runs
|
|
392
|
+
// when there's no parseable file, so jump to 95.
|
|
393
|
+
percent: 95,
|
|
394
|
+
message: 'No parseable files found — skipping parsing phase',
|
|
395
|
+
stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount },
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
// Sequential parsing has been removed: the worker pool (quarantine +
|
|
399
|
+
// respawn/recycle + circuit breaker) is the sole parse path. The three
|
|
400
|
+
// channels that used to select an in-process parser are now hard errors, so
|
|
401
|
+
// an operator who set one gets an actionable message instead of a silently
|
|
402
|
+
// slower (now nonexistent) fallback. Validated before any chunk work; a
|
|
403
|
+
// zero-parseable-file repo is exempt (nothing to parse).
|
|
404
|
+
if (totalParseable > 0) {
|
|
405
|
+
const requestedPoolSize = options?.workerPoolSize;
|
|
406
|
+
const disabledByEnv = requestedPoolSize === undefined && (0, worker_pool_js_1.workerPoolDisabledByEnv)();
|
|
407
|
+
if (options?.skipWorkers || requestedPoolSize === 0 || disabledByEnv) {
|
|
408
|
+
const reason = options?.skipWorkers
|
|
409
|
+
? '`skipWorkers: true` was passed'
|
|
410
|
+
: requestedPoolSize === 0
|
|
411
|
+
? '`--workers 0` (workerPoolSize=0) was requested'
|
|
412
|
+
: '`CGRAPH_WORKER_POOL_SIZE=0` is set';
|
|
413
|
+
throw new worker_pool_js_1.WorkerPoolDisabledError(`Worker-pool parsing cannot be disabled (${reason}). GitNexus no longer ` +
|
|
414
|
+
`has a sequential parser — the worker pool self-heals via quarantine + ` +
|
|
415
|
+
`respawn, so there is no slower path to fall back to. Pass ` +
|
|
416
|
+
`\`--workers <N>\` with N>=1, or omit it for an auto-sized pool.`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Build byte-budget chunks. The budget is resolved per-call (U14): options
|
|
420
|
+
// first, then env, then the built-in default. Pre-U14 this was a
|
|
421
|
+
// module-load IIFE constant, which froze the env value at import time
|
|
422
|
+
// and made `PipelineOptions.chunkByteBudget` silently no-op on warm test
|
|
423
|
+
// runs. Resolving in the function body restores per-call configurability
|
|
424
|
+
// and matches the pattern used by resolveAutoPoolSize and the U1
|
|
425
|
+
// parseChunkConcurrency resolver.
|
|
426
|
+
// Effective worker count, computed up-front so the chunk budget can scale to
|
|
427
|
+
// keep the whole pool busy (#worker-idle). The pool is ALWAYS used (sequential
|
|
428
|
+
// parsing was removed; the disabled channels threw above). Size it to the
|
|
429
|
+
// work: an explicit `--workers <N>` pins the size; otherwise the cores-based
|
|
430
|
+
// auto size is capped by the repo's worth of work (~one worker per
|
|
431
|
+
// CHUNK_BYTES_PER_WORKER of source) so a tiny repo spawns ~1 worker instead of
|
|
432
|
+
// a full pool, replacing the job the deleted small-repo threshold used to do.
|
|
433
|
+
// KTD-3 of the remove-sequential plan; the cap formula is intentionally coarse
|
|
434
|
+
// (tuning deferred).
|
|
435
|
+
const explicitPoolSize = options?.workerPoolSize;
|
|
436
|
+
const workProportionalCap = Math.max(1, Math.ceil(totalBytes / CHUNK_BYTES_PER_WORKER));
|
|
437
|
+
const effectivePoolSize = explicitPoolSize && explicitPoolSize > 0
|
|
438
|
+
? explicitPoolSize
|
|
439
|
+
: Math.min((0, worker_pool_js_1.resolveAutoPoolSize)(), workProportionalCap);
|
|
440
|
+
const chunkByteBudget = resolveChunkByteBudget(options, effectivePoolSize);
|
|
441
|
+
// Sub-batch size so each chunk fans into ~`TARGET_JOBS_PER_WORKER` jobs per
|
|
442
|
+
// worker, giving the pool's idle-slot assignment room to load-balance. An
|
|
443
|
+
// explicit `CGRAPH_WORKER_SUB_BATCH_MAX_BYTES` operator override wins.
|
|
444
|
+
const subBatchEnv = Number(process.env.CGRAPH_WORKER_SUB_BATCH_MAX_BYTES);
|
|
445
|
+
const dispatchSubBatchMaxBytes = Number.isFinite(subBatchEnv) && subBatchEnv > 0
|
|
446
|
+
? subBatchEnv
|
|
447
|
+
: Math.max(MIN_SUB_BATCH_BYTES, Math.ceil(chunkByteBudget / (effectivePoolSize * TARGET_JOBS_PER_WORKER)));
|
|
448
|
+
// Heap-scale guardrails (#2649), measured on a Linux-kernel analyze
|
|
449
|
+
// (94,773 files): ~55 graph nodes per parseable file and ~2.2KB of
|
|
450
|
+
// main-thread heap per node, linear across 113 chunks (see
|
|
451
|
+
// docs/plans/2026-07-23-gitnexus-plan-large-repo-analyze-oom.md §2).
|
|
452
|
+
// Estimates, not contracts — used only to warn early (preflight) and to
|
|
453
|
+
// convert a certain multi-minute GC death spiral into an immediate
|
|
454
|
+
// actionable error (mid-loop guard).
|
|
455
|
+
const projectedHeapNeedBytes = projectParseHeapNeedBytes(parseableScanned.length);
|
|
456
|
+
const heapLimitBytes = node_v8_1.default.getHeapStatistics().heap_size_limit;
|
|
457
|
+
if (projectedHeapNeedBytes > heapLimitBytes * PREFLIGHT_WARN_FRACTION) {
|
|
458
|
+
logger_js_1.logger.warn(`Large repository: analyzing ${parseableScanned.length} files needs roughly ${Math.round(projectedHeapNeedBytes / 1024 / 1024 / 1024)}GB of memory, ` +
|
|
459
|
+
`but Node is limited to ${Math.round(heapLimitBytes / 1024 / 1024 / 1024)}GB — analyze may stop early. ${heapPressureRemedy(heapLimitBytes)}`);
|
|
460
|
+
}
|
|
461
|
+
const chunks = [];
|
|
462
|
+
let currentChunk = [];
|
|
463
|
+
let currentBytes = 0;
|
|
464
|
+
for (const file of parseableScanned) {
|
|
465
|
+
if (currentChunk.length > 0 && currentBytes + file.size > chunkByteBudget) {
|
|
466
|
+
chunks.push(currentChunk);
|
|
467
|
+
currentChunk = [];
|
|
468
|
+
currentBytes = 0;
|
|
469
|
+
}
|
|
470
|
+
currentChunk.push(file.path);
|
|
471
|
+
currentBytes += file.size;
|
|
472
|
+
}
|
|
473
|
+
if (currentChunk.length > 0)
|
|
474
|
+
chunks.push(currentChunk);
|
|
475
|
+
const numChunks = chunks.length;
|
|
476
|
+
if (env_js_1.isDev) {
|
|
477
|
+
const totalMB = parseableScanned.reduce((s, f) => s + f.size, 0) / (1024 * 1024);
|
|
478
|
+
logger_js_1.logger.info(`📂 Scan: ${totalFiles} paths, ${totalParseable} parseable (${totalMB.toFixed(0)}MB), ${numChunks} chunks @ ${chunkByteBudget / (1024 * 1024)}MB budget`);
|
|
479
|
+
}
|
|
480
|
+
// Skip the "Parsing N files..." announcement when there's nothing to parse
|
|
481
|
+
// — the early-return branch above already emitted percent 95 ("skipping
|
|
482
|
+
// parsing phase"), and emitting percent 20 here would regress the
|
|
483
|
+
// progress stream non-monotonically (M2 from PR #1693 review).
|
|
484
|
+
if (totalParseable > 0) {
|
|
485
|
+
onProgress({
|
|
486
|
+
phase: 'parsing',
|
|
487
|
+
percent: 20,
|
|
488
|
+
message: `Parsing ${totalParseable} files in ${numChunks} chunk${numChunks !== 1 ? 's' : ''}...`,
|
|
489
|
+
stats: { filesProcessed: 0, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
// Create the worker pool lazily, reusing it across cache-miss chunks.
|
|
493
|
+
//
|
|
494
|
+
// KTD-8 — the pool is intentionally NOT created before the parse-cache
|
|
495
|
+
// lookup: a warm-cache all-hit run must replay cached worker output without
|
|
496
|
+
// loading parse-worker.js or any tree-sitter/N-API native bindings. So
|
|
497
|
+
// `getOrCreateWorkerPool` is called only from inside the chunk loop, on the
|
|
498
|
+
// first cache MISS. There is no longer a "should we use workers?" gate:
|
|
499
|
+
// sequential parsing was removed and the disabled channels (`--workers 0` /
|
|
500
|
+
// env=0 / `skipWorkers`) threw above, so for any repo with parseable files
|
|
501
|
+
// the pool is always the parse path.
|
|
502
|
+
let workerPool;
|
|
503
|
+
const getOrCreateWorkerPool = () => {
|
|
504
|
+
if (workerPool)
|
|
505
|
+
return workerPool;
|
|
506
|
+
try {
|
|
507
|
+
// Test-only injection: integration tests pass a custom worker script URL
|
|
508
|
+
// via `workerUrlForTest` so they can drive the chunk-loop with
|
|
509
|
+
// deterministically-misbehaving workers without mocking the module import
|
|
510
|
+
// graph. When unset, the normal src/ → dist/ resolution runs.
|
|
511
|
+
let workerUrl = options?.workerUrlForTest ?? new URL('../workers/parse-worker.js', (0, node_url_1.pathToFileURL)(__filename));
|
|
512
|
+
// When running under vitest, import.meta.url points to src/ where no .js exists.
|
|
513
|
+
// Fall back to the compiled dist/ worker so the pool can spawn real worker threads.
|
|
514
|
+
const thisDir = __dirname;
|
|
515
|
+
if (!options?.workerUrlForTest && !node_fs_1.default.existsSync((0, node_url_1.fileURLToPath)(workerUrl))) {
|
|
516
|
+
const distWorker = node_path_1.default.resolve(thisDir, '..', '..', '..', '..', 'dist', 'core', 'ingestion', 'workers', 'parse-worker.js');
|
|
517
|
+
if (node_fs_1.default.existsSync(distWorker)) {
|
|
518
|
+
workerUrl = (0, node_url_1.pathToFileURL)(distWorker);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Thread the ParsedFile store path into the pool so workers write their
|
|
522
|
+
// own shards (#1983 parallel serialization). `parsedFileStorePath` is
|
|
523
|
+
// declared below but this closure only runs from inside the chunk loop,
|
|
524
|
+
// after it is initialized; `undefined` drives the worker no-store
|
|
525
|
+
// fallback (return ParsedFiles in the result).
|
|
526
|
+
workerPool = (0, worker_pool_js_1.createWorkerPool)(workerUrl, effectivePoolSize, {
|
|
527
|
+
parsedFileStoreStoragePath: parsedFileStorePath,
|
|
528
|
+
// Durable, content-addressed shard dir for warm-cache reuse (#2038).
|
|
529
|
+
// Initialized below before the chunk loop (same deferred-init pattern
|
|
530
|
+
// as `parsedFileStorePath`); this closure only runs from the loop.
|
|
531
|
+
durableParsedFileStoragePath: durableParsedFileDir,
|
|
532
|
+
// Fan each chunk across the whole pool (#worker-idle): without this a
|
|
533
|
+
// chunk smaller than the 8 MB sub-batch cap became a single job on a
|
|
534
|
+
// single worker. Honors an explicit `subBatchMaxBytes` / env override.
|
|
535
|
+
subBatchMaxBytes: dispatchSubBatchMaxBytes,
|
|
536
|
+
});
|
|
537
|
+
return workerPool;
|
|
538
|
+
}
|
|
539
|
+
catch (err) {
|
|
540
|
+
// Pool *construction* failed (e.g. the worker script is missing — a
|
|
541
|
+
// broken install). Fail fast with the cause (#1741). There is no
|
|
542
|
+
// sequential parser to fall back to; the operator must fix the worker
|
|
543
|
+
// startup (commonly a missing build so dist/ has no parse-worker).
|
|
544
|
+
handleWorkerStartupFailure(err);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
let filesParsedSoFar = 0;
|
|
548
|
+
const exportedTypeMap = new Map();
|
|
549
|
+
const bindingAccumulator = new binding_accumulator_js_1.BindingAccumulator();
|
|
550
|
+
const allFetchCalls = [];
|
|
551
|
+
const allFetchWrapperDefs = [];
|
|
552
|
+
const allExtractedRoutes = [];
|
|
553
|
+
const allDecoratorRoutes = [];
|
|
554
|
+
const allRouterIncludes = [];
|
|
555
|
+
const allRouterImports = [];
|
|
556
|
+
const allRouterConstructorPrefixes = [];
|
|
557
|
+
const allRouterModuleAliases = [];
|
|
558
|
+
// Per-file Python module constants (#2391); resolved into decorator route paths
|
|
559
|
+
// below, after cross-file aggregation, alongside the include_router prefix pass.
|
|
560
|
+
const allModuleConstants = [];
|
|
561
|
+
const allSpringTypes = [];
|
|
562
|
+
const allToolDefs = [];
|
|
563
|
+
const allORMQueries = [];
|
|
564
|
+
// Aggregated per-file ParsedFile artifacts produced by workers' calls
|
|
565
|
+
// to `extractParsedFile`. Threaded through to the scope-resolution
|
|
566
|
+
// phase so it can SKIP its own re-extraction on cache hits — this is
|
|
567
|
+
// the second-half of the parse-cache speedup since scope-resolution's
|
|
568
|
+
// re-parse otherwise dominates the warm-cache wall-clock time.
|
|
569
|
+
const allParsedFiles = [];
|
|
570
|
+
// Content-addressed parse cache (spec §5.3 compliant — this is NOT the
|
|
571
|
+
// removed incremental index). The graph is rebuilt from scratch every run;
|
|
572
|
+
// this cache only skips tree-sitter worker dispatch + byte-copies the
|
|
573
|
+
// ParsedFile shards when a chunk's file contents are unchanged (chunkHash
|
|
574
|
+
// hit → replay cached ParseWorkerResult[], restore durable shards). Loaded
|
|
575
|
+
// before the run and persisted after by the caller (run-analyze.ts); this
|
|
576
|
+
// function mutates `entries` / `usedKeys` in place. See parse-cache.ts.
|
|
577
|
+
const parseCache = options?.parseCache;
|
|
578
|
+
// Disk-backed ParsedFile store (#1983): when a storage path is available we
|
|
579
|
+
// flush worker-produced ParsedFiles to disk per chunk (instead of retaining
|
|
580
|
+
// them in `allParsedFiles`) and scope-resolution streams them back per
|
|
581
|
+
// language — avoiding both the ~1× semantic-model RAM cost of holding them
|
|
582
|
+
// and, critically, the unbounded native tree-sitter re-parse leak that
|
|
583
|
+
// scope-resolution's main-thread re-extraction otherwise accumulates. When
|
|
584
|
+
// there is no storage path (tests / direct pipeline calls), we fall back to
|
|
585
|
+
// retaining them in `allParsedFiles` (small-repo path, preserves prior
|
|
586
|
+
// behavior). Cleared up-front so a prior run's shards never leak in.
|
|
587
|
+
const parsedFileStorePath = options?.parsedFileStorePath;
|
|
588
|
+
if (parsedFileStorePath)
|
|
589
|
+
await (0, parsedfile_store_js_1.clearParsedFileStore)(parsedFileStorePath);
|
|
590
|
+
// Durable, content-addressed ParsedFile store (#2038 warm-cache coverage) —
|
|
591
|
+
// a sibling of the run-scoped store, NOT cleared per run. Workers write a
|
|
592
|
+
// shard per chunk hash; on a warm parse-cache hit we restore the chunk's
|
|
593
|
+
// shards into the run-scoped store so scope-resolution streams them without
|
|
594
|
+
// re-parsing. `durableHitKeys` is the prior run's index, version-gated by
|
|
595
|
+
// PARSE_CACHE_VERSION (a mismatch ⇒ empty ⇒ every chunk re-dispatches, which
|
|
596
|
+
// repopulates the durable store — never the main-thread extract fallback).
|
|
597
|
+
const durableParsedFileDir = parsedFileStorePath !== undefined ? (0, parsedfile_store_js_1.getDurableParsedFileDir)(parsedFileStorePath) : undefined;
|
|
598
|
+
const durableHitKeys = durableParsedFileDir !== undefined
|
|
599
|
+
? await (0, parsedfile_store_js_1.loadDurableParsedFileIndex)(durableParsedFileDir, parse_cache_js_1.PARSE_CACHE_VERSION)
|
|
600
|
+
: new Set();
|
|
601
|
+
let chunkCacheHits = 0;
|
|
602
|
+
let chunkCacheMisses = 0;
|
|
603
|
+
try {
|
|
604
|
+
// U1 — bounded chunk concurrency (B1 from PR #1693 review): pre-fetch
|
|
605
|
+
// chunk file contents up to `parseChunkConcurrency` chunks ahead of the
|
|
606
|
+
// dispatch cursor so file I/O overlaps with worker compute. Worker
|
|
607
|
+
// dispatch itself stays serial because `WorkerPool.dispatch` is not
|
|
608
|
+
// reentrant (concurrent calls would race on the shared per-slot
|
|
609
|
+
// busy/in-flight state). With concurrency=1 behavior is identical to
|
|
610
|
+
// the pure-serial loop. F4: deferred-state aggregation still happens
|
|
611
|
+
// in chunkIdx order (the for-loop below iterates sequentially), so
|
|
612
|
+
// cross-chunk processors see deterministic input regardless of
|
|
613
|
+
// file-read completion order. Honors options.parseChunkConcurrency
|
|
614
|
+
// (threaded from the CLI), then CGRAPH_PARSE_CHUNK_CONCURRENCY env
|
|
615
|
+
// (default 2 — matches the help text the CLI advertises).
|
|
616
|
+
const parseChunkConcurrency = (() => {
|
|
617
|
+
const opt = options?.parseChunkConcurrency;
|
|
618
|
+
if (typeof opt === 'number' && Number.isInteger(opt) && opt >= 1)
|
|
619
|
+
return opt;
|
|
620
|
+
const env = Number(process.env.CGRAPH_PARSE_CHUNK_CONCURRENCY);
|
|
621
|
+
if (Number.isInteger(env) && env >= 1)
|
|
622
|
+
return env;
|
|
623
|
+
return 2;
|
|
624
|
+
})();
|
|
625
|
+
const chunkContentPromises = new Array(numChunks);
|
|
626
|
+
const startChunkPrefetch = (i) => {
|
|
627
|
+
if (i >= numChunks || chunkContentPromises[i] !== undefined)
|
|
628
|
+
return;
|
|
629
|
+
chunkContentPromises[i] = (0, filesystem_walker_js_1.readFileContents)(repoPath, chunks[i]);
|
|
630
|
+
};
|
|
631
|
+
for (let i = 0; i < Math.min(parseChunkConcurrency, numChunks); i++) {
|
|
632
|
+
startChunkPrefetch(i);
|
|
633
|
+
}
|
|
634
|
+
// Hoisted loop-invariant: CGRAPH_VERBOSE / NODE_ENV are read once
|
|
635
|
+
// (not on every chunk). Previously evaluated at the top of the loop
|
|
636
|
+
// body, which re-read process.env on every iteration even though
|
|
637
|
+
// the env can't change mid-run.
|
|
638
|
+
const verboseThroughputLog = env_js_1.isDev || (0, verbose_js_1.isVerboseIngestionEnabled)();
|
|
639
|
+
const heapProbeEveryN = (0, heap_probe_js_1.isDebugHeapEnabled)() ? 25 : 0;
|
|
640
|
+
let pendingWorkerChunk = null;
|
|
641
|
+
// Apply one chunk's merged worker data: per-chunk aggregation into the
|
|
642
|
+
// run-level accumulators + the throughput log. Shared by the cache-hit
|
|
643
|
+
// (inline) and worker (deferred) paths. The `| null` guard is defensive —
|
|
644
|
+
// every live caller passes real worker data now that sequential parsing
|
|
645
|
+
// (which was the only path that passed null) is gone.
|
|
646
|
+
const applyChunkResults = async (chunkWorkerData, chunkIdx, chunkFiles, chunkStartMs) => {
|
|
647
|
+
if (chunkWorkerData) {
|
|
648
|
+
if (chunkWorkerData.parsedFiles?.length) {
|
|
649
|
+
if (parsedFileStorePath) {
|
|
650
|
+
await (0, parsedfile_store_js_1.persistParsedFileChunk)(parsedFileStorePath, `chunk-${chunkIdx}`, chunkWorkerData.parsedFiles);
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
for (const item of chunkWorkerData.parsedFiles)
|
|
654
|
+
allParsedFiles.push(item);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (chunkWorkerData.fileScopeBindings?.length) {
|
|
658
|
+
for (const { filePath, bindings } of chunkWorkerData.fileScopeBindings) {
|
|
659
|
+
if (typeof filePath !== 'string' || filePath.length === 0)
|
|
660
|
+
continue;
|
|
661
|
+
if (!Array.isArray(bindings))
|
|
662
|
+
continue;
|
|
663
|
+
const entries = [];
|
|
664
|
+
for (const tuple of bindings) {
|
|
665
|
+
if (!Array.isArray(tuple) || tuple.length !== 2)
|
|
666
|
+
continue;
|
|
667
|
+
const [varName, typeName] = tuple;
|
|
668
|
+
if (typeof varName !== 'string' || typeof typeName !== 'string')
|
|
669
|
+
continue;
|
|
670
|
+
entries.push({ scope: '', varName, typeName });
|
|
671
|
+
}
|
|
672
|
+
if (entries.length > 0) {
|
|
673
|
+
bindingAccumulator.appendFile(filePath, entries);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (chunkWorkerData.fetchCalls?.length) {
|
|
678
|
+
for (const item of chunkWorkerData.fetchCalls)
|
|
679
|
+
allFetchCalls.push(item);
|
|
680
|
+
}
|
|
681
|
+
if (chunkWorkerData.fetchWrapperDefs?.length) {
|
|
682
|
+
for (const item of chunkWorkerData.fetchWrapperDefs)
|
|
683
|
+
allFetchWrapperDefs.push(item);
|
|
684
|
+
}
|
|
685
|
+
if (chunkWorkerData.routes?.length) {
|
|
686
|
+
for (const item of chunkWorkerData.routes)
|
|
687
|
+
allExtractedRoutes.push(item);
|
|
688
|
+
}
|
|
689
|
+
if (chunkWorkerData.decoratorRoutes?.length) {
|
|
690
|
+
for (const item of chunkWorkerData.decoratorRoutes)
|
|
691
|
+
allDecoratorRoutes.push(item);
|
|
692
|
+
}
|
|
693
|
+
if (chunkWorkerData.routerIncludes?.length) {
|
|
694
|
+
for (const item of chunkWorkerData.routerIncludes)
|
|
695
|
+
allRouterIncludes.push(item);
|
|
696
|
+
}
|
|
697
|
+
if (chunkWorkerData.routerImports?.length) {
|
|
698
|
+
for (const item of chunkWorkerData.routerImports)
|
|
699
|
+
allRouterImports.push(item);
|
|
700
|
+
}
|
|
701
|
+
if (chunkWorkerData.routerConstructorPrefixes?.length) {
|
|
702
|
+
for (const item of chunkWorkerData.routerConstructorPrefixes) {
|
|
703
|
+
allRouterConstructorPrefixes.push(item);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (chunkWorkerData.routerModuleAliases?.length) {
|
|
707
|
+
for (const item of chunkWorkerData.routerModuleAliases)
|
|
708
|
+
allRouterModuleAliases.push(item);
|
|
709
|
+
}
|
|
710
|
+
if (chunkWorkerData.moduleConstants?.length) {
|
|
711
|
+
for (const item of chunkWorkerData.moduleConstants)
|
|
712
|
+
allModuleConstants.push(item);
|
|
713
|
+
}
|
|
714
|
+
if (chunkWorkerData.springTypes?.length) {
|
|
715
|
+
for (const item of chunkWorkerData.springTypes)
|
|
716
|
+
allSpringTypes.push(item);
|
|
717
|
+
}
|
|
718
|
+
if (chunkWorkerData.toolDefs?.length) {
|
|
719
|
+
for (const item of chunkWorkerData.toolDefs)
|
|
720
|
+
allToolDefs.push(item);
|
|
721
|
+
}
|
|
722
|
+
if (chunkWorkerData.ormQueries?.length) {
|
|
723
|
+
for (const item of chunkWorkerData.ormQueries)
|
|
724
|
+
allORMQueries.push(item);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
filesParsedSoFar += chunkFiles.length;
|
|
728
|
+
if (verboseThroughputLog && chunkStartMs !== null) {
|
|
729
|
+
const elapsedMs = Date.now() - chunkStartMs;
|
|
730
|
+
const filesPerSec = elapsedMs > 0 ? (chunkFiles.length * 1000) / elapsedMs : 0;
|
|
731
|
+
const stats = workerPool?.getStats?.();
|
|
732
|
+
const poolFrag = stats
|
|
733
|
+
? ` pool: ${stats.activeSlots}/${stats.size} active, ` +
|
|
734
|
+
`${stats.quarantined} quarantined${stats.poolBroken ? ', BROKEN' : ''}`
|
|
735
|
+
: ' (cache replay)';
|
|
736
|
+
logger_js_1.logger.info(`📊 chunk ${chunkIdx + 1}/${numChunks}: ${chunkFiles.length} files in ${elapsedMs}ms ` +
|
|
737
|
+
`(${filesPerSec.toFixed(1)} files/s)${poolFrag}`);
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
// Merge + finalize a parked worker chunk: graph merge (the overlapped
|
|
741
|
+
// main-thread step) → parse-cache write-guard → run-level aggregation.
|
|
742
|
+
const finalizeWorkerChunk = async (p) => {
|
|
743
|
+
const chunkWorkerData = (0, parsing_processor_js_1.mergeChunkResults)(graph, symbolTable, p.rawResults, exportedTypeMap);
|
|
744
|
+
// Persist raw results for this chunk hash (skipping when any chunk file
|
|
745
|
+
// was worker-quarantined, so the narrower rawResults isn't cached under
|
|
746
|
+
// the full-chunk key).
|
|
747
|
+
if (parseCache && p.chunkHash && p.rawResults.length > 0) {
|
|
748
|
+
const quarantineSet = new Set(workerPool?.getQuarantinedPaths?.() ?? []);
|
|
749
|
+
const chunkHadQuarantine = p.chunkFiles.some((f) => quarantineSet.has(f.path));
|
|
750
|
+
if (chunkHadQuarantine) {
|
|
751
|
+
if (env_js_1.isDev) {
|
|
752
|
+
const quarantinedInChunk = p.chunkFiles.filter((f) => quarantineSet.has(f.path)).length;
|
|
753
|
+
logger_js_1.logger.info(`📦 parse-cache SKIP: chunk ${p.chunkIdx + 1}/${numChunks} ` +
|
|
754
|
+
`had ${quarantinedInChunk} worker-quarantined file(s); ` +
|
|
755
|
+
`next run will rediscover (${p.chunkHash.slice(0, 8)})`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
await (0, parse_cache_js_1.persistParseCacheChunk)(parseCache, p.chunkHash, p.rawResults);
|
|
760
|
+
if (env_js_1.isDev) {
|
|
761
|
+
logger_js_1.logger.info(`📦 parse-cache MISS+store: chunk ${p.chunkIdx + 1}/${numChunks} (${p.chunkFiles.length} files, ${p.chunkHash.slice(0, 8)})`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
await applyChunkResults(chunkWorkerData, p.chunkIdx, p.chunkFiles, p.chunkStartMs);
|
|
766
|
+
};
|
|
767
|
+
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
768
|
+
if (heapProbeEveryN > 0 && chunkIdx > 0 && chunkIdx % heapProbeEveryN === 0) {
|
|
769
|
+
(0, heap_probe_js_1.logHeapProbe)(`parse-chunk-${chunkIdx}`, `nodes=${graph.nodeCount} parsedFiles=${allParsedFiles.length}`);
|
|
770
|
+
}
|
|
771
|
+
// #2649 mid-loop heap guard: fail actionably BEFORE V8 enters the
|
|
772
|
+
// ineffective-mark-compact death spiral (which also falsely times out
|
|
773
|
+
// healthy workers). The pool is torn down by this function's finally.
|
|
774
|
+
const heapUsedNow = process.memoryUsage().heapUsed;
|
|
775
|
+
const heapLimitNow = node_v8_1.default.getHeapStatistics().heap_size_limit;
|
|
776
|
+
if (shouldAbortForHeapPressure(heapUsedNow, heapLimitNow)) {
|
|
777
|
+
throw new Error(`Analyze stopped before running out of memory: ${Math.round(heapUsedNow / 1024 / 1024)}MB of the ` +
|
|
778
|
+
`${Math.round(heapLimitNow / 1024 / 1024)}MB Node heap in use at parse chunk ${chunkIdx + 1}/${numChunks} (#2649). ` +
|
|
779
|
+
heapPressureRemedy(heapLimitNow));
|
|
780
|
+
}
|
|
781
|
+
const chunkPaths = chunks[chunkIdx];
|
|
782
|
+
// Start wall-clock for the per-chunk throughput log emitted at end
|
|
783
|
+
// of this iteration. The gate is computed once above; here we just
|
|
784
|
+
// sample the clock if the gate is on. Computed when either
|
|
785
|
+
// NODE_ENV=development OR the operator passed `--verbose`
|
|
786
|
+
// (CGRAPH_VERBOSE) — the previous `isDev`-only gate meant
|
|
787
|
+
// operators running `cgraph analyze --verbose` in production
|
|
788
|
+
// never saw the log (M3 from PR #1693 review).
|
|
789
|
+
const chunkStartMs = verboseThroughputLog ? Date.now() : null;
|
|
790
|
+
const chunkContentPromise = chunkContentPromises[chunkIdx];
|
|
791
|
+
if (!chunkContentPromise) {
|
|
792
|
+
throw new Error(`Missing prefetched parse chunk ${chunkIdx + 1}/${numChunks}`);
|
|
793
|
+
}
|
|
794
|
+
const chunkContents = await chunkContentPromise;
|
|
795
|
+
chunkContentPromises[chunkIdx] = undefined; // release the in-memory copy
|
|
796
|
+
startChunkPrefetch(chunkIdx + parseChunkConcurrency);
|
|
797
|
+
const chunkFiles = [];
|
|
798
|
+
for (const p of chunkPaths) {
|
|
799
|
+
const content = chunkContents.get(p);
|
|
800
|
+
if (content !== undefined)
|
|
801
|
+
chunkFiles.push({ path: p, content });
|
|
802
|
+
}
|
|
803
|
+
// Compute the chunk's content-hash signature (if cache available).
|
|
804
|
+
let chunkHash = null;
|
|
805
|
+
if (parseCache) {
|
|
806
|
+
const entries = chunkFiles.map((f) => ({
|
|
807
|
+
filePath: f.path,
|
|
808
|
+
contentHash: (0, parse_cache_js_1.fileContentHash)(f.content),
|
|
809
|
+
}));
|
|
810
|
+
chunkHash = (0, parse_cache_js_1.computeChunkHash)(entries, false);
|
|
811
|
+
}
|
|
812
|
+
const cachedRaw = chunkHash && parseCache ? await (0, parse_cache_js_1.loadParseCacheChunk)(parseCache, chunkHash) : undefined;
|
|
813
|
+
// Track every chunk hash we touched so the orchestrator can
|
|
814
|
+
// prune stale entries (chunks whose composition no longer
|
|
815
|
+
// corresponds to a live chunk in the current scan) before saving.
|
|
816
|
+
if (parseCache && chunkHash)
|
|
817
|
+
parseCache.usedKeys.add(chunkHash);
|
|
818
|
+
// A parse-cache hit may skip the workers ONLY if the chunk's ParsedFiles
|
|
819
|
+
// are recoverable without a main-thread re-parse: restored from a durable
|
|
820
|
+
// shard (store path) or carried in the cached result (no-store path). If a
|
|
821
|
+
// cached chunk's durable shards are missing — first run after the durable
|
|
822
|
+
// store was introduced, or a pruned/version-stale shard — fall through to
|
|
823
|
+
// a worker re-dispatch to repopulate them. NEVER let scope-resolution
|
|
824
|
+
// re-extract on the main thread (the #1983 OOM the durable store closes).
|
|
825
|
+
const durableHit = chunkHash !== null && durableParsedFileDir !== undefined && durableHitKeys.has(chunkHash);
|
|
826
|
+
if (cachedRaw && cachedRaw.length > 0 && (durableHit || parsedFileStorePath === undefined)) {
|
|
827
|
+
// Cache hit: replay cached worker output. Finalize any parked worker
|
|
828
|
+
// chunk FIRST so deferred aggregation stays in chunk order, then merge
|
|
829
|
+
// + apply this hit inline (no worker dispatch to overlap).
|
|
830
|
+
if (pendingWorkerChunk) {
|
|
831
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
832
|
+
pendingWorkerChunk = null;
|
|
833
|
+
}
|
|
834
|
+
chunkCacheHits++;
|
|
835
|
+
const chunkWorkerData = (0, parsing_processor_js_1.mergeChunkResults)(graph, symbolTable, cachedRaw, exportedTypeMap);
|
|
836
|
+
if (env_js_1.isDev) {
|
|
837
|
+
logger_js_1.logger.info(`📦 parse-cache HIT: chunk ${chunkIdx + 1}/${numChunks} (${chunkFiles.length} files, ${chunkHash?.slice(0, 8) ?? 'unknown'})`);
|
|
838
|
+
}
|
|
839
|
+
// Progress update so UI advances even on a cache hit.
|
|
840
|
+
const cachedFiles = chunkFiles.length;
|
|
841
|
+
onProgress({
|
|
842
|
+
phase: 'parsing',
|
|
843
|
+
// Parse phase covers 20-70 (50 points). Deferred extraction below
|
|
844
|
+
// takes 70-95 so the UI advances through the (potentially long)
|
|
845
|
+
// resolution stages instead of holding at 82 (M2 from PR #1693
|
|
846
|
+
// review).
|
|
847
|
+
percent: Math.round(20 + ((filesParsedSoFar + cachedFiles) / totalParseable) * 50),
|
|
848
|
+
message: `Parsing chunk ${chunkIdx + 1}/${numChunks} (cache)...`,
|
|
849
|
+
stats: {
|
|
850
|
+
filesProcessed: filesParsedSoFar + cachedFiles,
|
|
851
|
+
totalFiles: totalParseable,
|
|
852
|
+
nodesCreated: graph.nodeCount,
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
// Restore the chunk's durable ParsedFile shards into the run-scoped
|
|
856
|
+
// store so scope-resolution finds full coverage with ZERO main-thread
|
|
857
|
+
// re-parse. A verbatim byte copy — byte-identical to a cold run.
|
|
858
|
+
if (durableHit && durableParsedFileDir && parsedFileStorePath && chunkHash) {
|
|
859
|
+
const restored = await (0, parsedfile_store_js_1.restoreDurableParsedFileShard)(durableParsedFileDir, parsedFileStorePath, chunkHash);
|
|
860
|
+
if (restored === 0) {
|
|
861
|
+
logger_js_1.logger.warn(`parsedfile-cache: durable shards missing for cached chunk ` +
|
|
862
|
+
`${chunkHash.slice(0, 8)} — scope-resolution will re-extract these files`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
await applyChunkResults(chunkWorkerData, chunkIdx, chunkFiles, chunkStartMs);
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
// Cache miss: dispatch to workers, capture the raw results, store
|
|
869
|
+
// them under the chunk hash for the next run.
|
|
870
|
+
chunkCacheMisses++;
|
|
871
|
+
if (durableParsedFileDir !== undefined && chunkHash !== null) {
|
|
872
|
+
try {
|
|
873
|
+
await (0, parsedfile_store_js_1.prepareDurableParsedFileChunk)(durableParsedFileDir, chunkHash);
|
|
874
|
+
}
|
|
875
|
+
catch (err) {
|
|
876
|
+
// The durable store is an optimization — degrade like the restore
|
|
877
|
+
// path does instead of failing the analyze. Workers recreate the
|
|
878
|
+
// directory on write, so at worst the old generation lingers.
|
|
879
|
+
logger_js_1.logger.warn({ err, chunkHash: chunkHash.slice(0, 8) }, 'parsedfile-cache: could not reset durable chunk generation; continuing');
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
const progressForChunk = (current, _total, filePath) => {
|
|
883
|
+
const globalCurrent = filesParsedSoFar + current;
|
|
884
|
+
// Parse phase covers 20-70 (M2). Deferred extraction handles 70-95.
|
|
885
|
+
const parsingProgress = 20 + (globalCurrent / totalParseable) * 50;
|
|
886
|
+
onProgress({
|
|
887
|
+
phase: 'parsing',
|
|
888
|
+
percent: Math.round(parsingProgress),
|
|
889
|
+
message: `Parsing chunk ${chunkIdx + 1}/${numChunks}...`,
|
|
890
|
+
detail: filePath,
|
|
891
|
+
stats: {
|
|
892
|
+
filesProcessed: globalCurrent,
|
|
893
|
+
totalFiles: totalParseable,
|
|
894
|
+
nodesCreated: graph.nodeCount,
|
|
895
|
+
},
|
|
896
|
+
});
|
|
897
|
+
};
|
|
898
|
+
const activeWorkerPool = getOrCreateWorkerPool();
|
|
899
|
+
// Worker path — PIPELINE: kick off this chunk's dispatch, merge the
|
|
900
|
+
// PREVIOUS chunk while these workers parse, then park this chunk for
|
|
901
|
+
// the next iteration to merge (overlapping its parse). The deferred
|
|
902
|
+
// merge + parse-cache write-guard + aggregation all run in
|
|
903
|
+
// `finalizeWorkerChunk`, in chunk order. The pool is the sole parse
|
|
904
|
+
// path — `getOrCreateWorkerPool` returns a pool or throws.
|
|
905
|
+
const dispatchPromise = (0, parsing_processor_js_1.dispatchChunkParse)(chunkFiles, activeWorkerPool, progressForChunk, undefined, chunkHash ?? undefined);
|
|
906
|
+
// Mark handled so a rejection during the overlap drain below isn't
|
|
907
|
+
// flagged as unhandled; the `await` re-throws it for real handling.
|
|
908
|
+
dispatchPromise.catch(() => { });
|
|
909
|
+
if (pendingWorkerChunk) {
|
|
910
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
911
|
+
pendingWorkerChunk = null;
|
|
912
|
+
}
|
|
913
|
+
let chunkResults;
|
|
914
|
+
try {
|
|
915
|
+
chunkResults = await dispatchPromise;
|
|
916
|
+
}
|
|
917
|
+
catch (err) {
|
|
918
|
+
if (!(err instanceof worker_pool_js_1.WorkerPoolInitializationError))
|
|
919
|
+
throw err;
|
|
920
|
+
// Every worker crashed during startup and the pool's bounded
|
|
921
|
+
// self-heal was exhausted. Fail fast (#1741) — there is no sequential
|
|
922
|
+
// parser to degrade to. `handleWorkerStartupFailure` always throws, so
|
|
923
|
+
// `chunkResults` stays definitely assigned for the parked chunk below.
|
|
924
|
+
handleWorkerStartupFailure(err);
|
|
925
|
+
}
|
|
926
|
+
pendingWorkerChunk = {
|
|
927
|
+
rawResults: chunkResults,
|
|
928
|
+
chunkIdx,
|
|
929
|
+
chunkHash,
|
|
930
|
+
chunkFiles,
|
|
931
|
+
chunkStartMs,
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
// (Per-chunk aggregation + parse-cache write + throughput log now run in
|
|
935
|
+
// `applyChunkResults` / `finalizeWorkerChunk` — see the merge-pipelining
|
|
936
|
+
// block above. Route/import/inheritance edges are emitted later: route
|
|
937
|
+
// resolution in the single end-of-loop pass below, the rest by the
|
|
938
|
+
// scope-resolution phase, RING4-2 #943.)
|
|
939
|
+
}
|
|
940
|
+
// Drain the final parked worker chunk — the last pipelined chunk has no
|
|
941
|
+
// successor to overlap its merge with, so merge + finalize it here.
|
|
942
|
+
if (pendingWorkerChunk) {
|
|
943
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
944
|
+
pendingWorkerChunk = null;
|
|
945
|
+
}
|
|
946
|
+
// The summary line is always logged (not gated by isDev) because it is the
|
|
947
|
+
// ONLY production-visible signal that the content-addressed parse cache is
|
|
948
|
+
// alive — one line per run, zero noise. Without it a warm re-analyze shows
|
|
949
|
+
// no evidence of cache hits and the modest total speedup looks like OS
|
|
950
|
+
// file caching, which has caused misdiagnosis of the cache as broken.
|
|
951
|
+
// Per-chunk HIT/MISS+store logs stay dev-only (verbose on large repos).
|
|
952
|
+
if (parseCache && (chunkCacheHits > 0 || chunkCacheMisses > 0)) {
|
|
953
|
+
logger_js_1.logger.info(`📦 parse-cache summary: ${chunkCacheHits} chunk hit(s), ${chunkCacheMisses} miss(es) across ${numChunks} chunk(s)`);
|
|
954
|
+
}
|
|
955
|
+
(0, heap_probe_js_1.logHeapProbe)('post-parse-chunks', `routes=${allExtractedRoutes.length} nodes=${graph.nodeCount} parsedFiles=${allParsedFiles.length}`);
|
|
956
|
+
// Deferred end-of-loop extraction (moved out of the per-chunk block):
|
|
957
|
+
// 1. route resolution on all chunks' routes
|
|
958
|
+
// Resolution sees the full repo graph instead of just current-and-earlier
|
|
959
|
+
// chunks. Import, call, and inheritance edges are emitted by the
|
|
960
|
+
// scope-resolution phase, not here (RING4-1 #942 removed the legacy call
|
|
961
|
+
// DAG; RING4-2 #943 removed the legacy import-map resolution + wildcard
|
|
962
|
+
// synthesis). Progress band: the route stage gets a slice of the 70-95
|
|
963
|
+
// range; a zero-length input leaves its band as a no-op jump.
|
|
964
|
+
// routes: 80 -> 85 (5)
|
|
965
|
+
const deferredProfile = (0, deferred_resolution_profile_js_1.isDeferredResolutionProfileEnabled)();
|
|
966
|
+
if (deferredProfile) {
|
|
967
|
+
(0, deferred_resolution_profile_js_1.logDeferredProfile)(`deferred band start: routes=${allExtractedRoutes.length}`);
|
|
968
|
+
}
|
|
969
|
+
// Populate `exportedTypeMap` from the in-progress graph so the post-parse
|
|
970
|
+
// enrichment pass (enrichExportedTypeMap) sees cross-file export types.
|
|
971
|
+
if (exportedTypeMap.size === 0 && graph.nodeCount > 0) {
|
|
972
|
+
(0, heap_probe_js_1.logHeapProbe)('pre-buildExportedTypeMapFromGraph');
|
|
973
|
+
const graphExports = (0, call_processor_js_1.buildExportedTypeMapFromGraph)(graph, model.symbols);
|
|
974
|
+
for (const [fp, exports] of graphExports)
|
|
975
|
+
exportedTypeMap.set(fp, exports);
|
|
976
|
+
(0, heap_probe_js_1.logHeapProbe)('post-buildExportedTypeMapFromGraph');
|
|
977
|
+
}
|
|
978
|
+
// Whole-repo, cross-file route extraction (e.g. Django) runs on the main
|
|
979
|
+
// thread — the worker has no filesystem access and can't follow `include()`
|
|
980
|
+
// chains across files. Merge its routes in before `processRoutesFromExtracted`
|
|
981
|
+
// and the routes phase consume `allExtractedRoutes`.
|
|
982
|
+
const crossFileRoutes = await extractCrossFileRoutes(allPaths, repoPath);
|
|
983
|
+
if (crossFileRoutes.length > 0) {
|
|
984
|
+
for (const r of crossFileRoutes)
|
|
985
|
+
allExtractedRoutes.push(r);
|
|
986
|
+
if (deferredProfile) {
|
|
987
|
+
(0, deferred_resolution_profile_js_1.logDeferredProfile)(`cross-file routes: +${crossFileRoutes.length}`);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (allExtractedRoutes.length > 0) {
|
|
991
|
+
const tRoutes = (0, deferred_resolution_profile_js_1.startTimer)(deferredProfile);
|
|
992
|
+
await (0, call_processor_js_1.processRoutesFromExtracted)(graph, allExtractedRoutes, model, (current, total) => {
|
|
993
|
+
const ratio = total > 0 ? current / total : 1;
|
|
994
|
+
onProgress({
|
|
995
|
+
phase: 'parsing',
|
|
996
|
+
percent: 80 + Math.round(ratio * 5),
|
|
997
|
+
message: 'Resolving routes (all chunks)...',
|
|
998
|
+
detail: `${current}/${total} routes`,
|
|
999
|
+
stats: {
|
|
1000
|
+
filesProcessed: filesParsedSoFar,
|
|
1001
|
+
totalFiles: totalParseable,
|
|
1002
|
+
nodesCreated: graph.nodeCount,
|
|
1003
|
+
},
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
(0, deferred_resolution_profile_js_1.endTimer)(tRoutes, (ms) => `processRoutesFromExtracted: ${ms.toFixed(0)}ms (${allExtractedRoutes.length} routes)`);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
finally {
|
|
1010
|
+
await workerPool?.terminate();
|
|
1011
|
+
}
|
|
1012
|
+
// Fetch calls + ORM queries were already extracted inside each worker
|
|
1013
|
+
// (returned in ParseWorkerResult, aggregated per chunk in applyChunkResults).
|
|
1014
|
+
// With sequential parsing removed there is no post-loop drain to run — only
|
|
1015
|
+
// the TypeEnv finalize + enrichment that the drain's `finally` used to host.
|
|
1016
|
+
// Finalize the accumulator and propagate any fixpoint-inferred exports before
|
|
1017
|
+
// `crossFile` disposes it downstream. Wrapped in try/catch so a cleanup
|
|
1018
|
+
// failure never masks a real parse error; disposal stays with `crossFile`.
|
|
1019
|
+
try {
|
|
1020
|
+
bindingAccumulator.finalize();
|
|
1021
|
+
const enriched = (0, binding_accumulator_js_1.enrichExportedTypeMap)(bindingAccumulator, graph, exportedTypeMap);
|
|
1022
|
+
if (env_js_1.isDev && enriched > 0) {
|
|
1023
|
+
logger_js_1.logger.info(`🔗 Worker TypeEnv enrichment: ${enriched} fixpoint-inferred exports added to ExportedTypeMap`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
catch (enrichErr) {
|
|
1027
|
+
if (env_js_1.isDev) {
|
|
1028
|
+
logger_js_1.logger.warn({ err: enrichErr.message }, 'Post-parse finalize/enrich failed during cleanup:');
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
// Worker-path enrichment: if exportedTypeMap is empty (e.g. the worker pool
|
|
1032
|
+
// built TypeEnv inside workers without access to SymbolTable), reconstruct
|
|
1033
|
+
// the map from graph nodes + SymbolTable here in the main thread before
|
|
1034
|
+
// handing the (now read-only) map to downstream phases. Doing it here means
|
|
1035
|
+
// crossFile receives a fully-populated map and never needs to mutate it for
|
|
1036
|
+
// initial-graph enrichment.
|
|
1037
|
+
if (exportedTypeMap.size === 0 && graph.nodeCount > 0) {
|
|
1038
|
+
const graphExports = (0, call_processor_js_1.buildExportedTypeMapFromGraph)(graph, model.symbols);
|
|
1039
|
+
for (const [fp, exports] of graphExports)
|
|
1040
|
+
exportedTypeMap.set(fp, exports);
|
|
1041
|
+
}
|
|
1042
|
+
// FastAPI router-prefix resolution (cross-file).
|
|
1043
|
+
//
|
|
1044
|
+
// #2391: resolve non-literal FastAPI decorator route paths (imported/composed
|
|
1045
|
+
// string constants) BEFORE the include_router/APIRouter prefix pass below, so a
|
|
1046
|
+
// resolved path is then prefix-joined like any literal path. Each such route
|
|
1047
|
+
// carries `routePathExpr`/`routePathOperands` and an empty `routePath`; we fold
|
|
1048
|
+
// the operands against the repo-wide, file-path-keyed constant map. On failure
|
|
1049
|
+
// we DROP the route (KTD5 skip floor) rather than emit a phantom `POST /`.
|
|
1050
|
+
if (allDecoratorRoutes.some((dr) => dr.routePathExpr !== undefined)) {
|
|
1051
|
+
const repoConstants = new Map();
|
|
1052
|
+
for (const { filePath, constants } of allModuleConstants) {
|
|
1053
|
+
repoConstants.set(filePath, constants);
|
|
1054
|
+
}
|
|
1055
|
+
const resolvedRoutes = [];
|
|
1056
|
+
let skipped = 0;
|
|
1057
|
+
for (const dr of allDecoratorRoutes) {
|
|
1058
|
+
if (dr.routePathExpr === undefined) {
|
|
1059
|
+
resolvedRoutes.push(dr);
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
const value = dr.routePathOperands
|
|
1063
|
+
? (0, python_const_resolver_js_1.resolveOperands)(dr.filePath, dr.routePathOperands, repoConstants)
|
|
1064
|
+
: null;
|
|
1065
|
+
if (value === null) {
|
|
1066
|
+
skipped++;
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
resolvedRoutes.push({ ...dr, routePath: value });
|
|
1070
|
+
}
|
|
1071
|
+
allDecoratorRoutes.length = 0;
|
|
1072
|
+
for (const dr of resolvedRoutes)
|
|
1073
|
+
allDecoratorRoutes.push(dr);
|
|
1074
|
+
if (env_js_1.isDev && skipped > 0) {
|
|
1075
|
+
logger_js_1.logger.info(` 🧩 Resolved composed route constants; ${skipped} unresolved route(s) skipped`);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
// Workers emit two kinds of records per Python file:
|
|
1079
|
+
// • `routerIncludes` — every `app.include_router(<routerExpr>, prefix='/x')`
|
|
1080
|
+
// site, where `routerExpr` is either `<module>.router` (Shape A) or a
|
|
1081
|
+
// bare local name (Shape B).
|
|
1082
|
+
// • `routerImports` — every `from <module> import router [as <alias>]`,
|
|
1083
|
+
// mapping a local name to a module key (the basename of the source
|
|
1084
|
+
// module). These let us resolve Shape-B router includes back to the
|
|
1085
|
+
// module that defines the router.
|
|
1086
|
+
//
|
|
1087
|
+
// We build `module-basename → Set<prefix>` and then walk
|
|
1088
|
+
// `allDecoratorRoutes`: any decorator route emitted from a `router.<verb>`
|
|
1089
|
+
// decorator inherits its file-basename's prefix. When a router is mounted
|
|
1090
|
+
// under multiple prefixes we duplicate the route entry, mirroring FastAPI's
|
|
1091
|
+
// runtime behaviour.
|
|
1092
|
+
if ((allRouterIncludes.length > 0 || allRouterConstructorPrefixes.length > 0) &&
|
|
1093
|
+
allDecoratorRoutes.length > 0) {
|
|
1094
|
+
const importsByFile = new Map();
|
|
1095
|
+
for (const imp of allRouterImports) {
|
|
1096
|
+
let m = importsByFile.get(imp.filePath);
|
|
1097
|
+
if (!m) {
|
|
1098
|
+
m = new Map();
|
|
1099
|
+
importsByFile.set(imp.filePath, m);
|
|
1100
|
+
}
|
|
1101
|
+
m.set(imp.localName, {
|
|
1102
|
+
moduleKey: imp.moduleKey,
|
|
1103
|
+
moduleKeyLong: imp.moduleKeyLong,
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
// Module-alias map keyed by file: `localName` (the imported module
|
|
1107
|
+
// identifier in this file) → long key. Shape-A receivers like
|
|
1108
|
+
// `users.router` are matched against this map; the long key, when
|
|
1109
|
+
// present, scopes the prefix to the precise source file.
|
|
1110
|
+
const moduleAliasesByFile = new Map();
|
|
1111
|
+
for (const alias of allRouterModuleAliases) {
|
|
1112
|
+
let m = moduleAliasesByFile.get(alias.filePath);
|
|
1113
|
+
if (!m) {
|
|
1114
|
+
m = new Map();
|
|
1115
|
+
moduleAliasesByFile.set(alias.filePath, m);
|
|
1116
|
+
}
|
|
1117
|
+
m.set(alias.localName, alias.moduleKeyLong);
|
|
1118
|
+
}
|
|
1119
|
+
// Two parallel maps: long-key (precise) and short-key (basename
|
|
1120
|
+
// fallback). Long-key entries are preferred when the file's own long
|
|
1121
|
+
// key matches; short-key entries match any file with that basename and
|
|
1122
|
+
// remain the fallback when no long key is known (e.g. Shape A includes
|
|
1123
|
+
// without a corresponding import statement).
|
|
1124
|
+
const prefixesByLongKey = new Map();
|
|
1125
|
+
const prefixesByShortKey = new Map();
|
|
1126
|
+
// Constructor prefixes are `router`-only (the apply gate below and the
|
|
1127
|
+
// group-layer tree-sitter both pin to the literal name `router`), so a
|
|
1128
|
+
// flat file-key → prefix map suffices — mirrors the group layer's shape.
|
|
1129
|
+
const constructorPrefixesByLongKey = new Map();
|
|
1130
|
+
const constructorPrefixesByShortKey = new Map();
|
|
1131
|
+
const recordPrefix = (target, key, prefix) => {
|
|
1132
|
+
let set = target.get(key);
|
|
1133
|
+
if (!set) {
|
|
1134
|
+
set = new Set();
|
|
1135
|
+
target.set(key, set);
|
|
1136
|
+
}
|
|
1137
|
+
set.add(prefix);
|
|
1138
|
+
};
|
|
1139
|
+
for (const inc of allRouterIncludes) {
|
|
1140
|
+
// Shape A: `<module>.router`. The worker emits `routerExpr` already
|
|
1141
|
+
// including `.router`, so split it back. We only know a short module
|
|
1142
|
+
// key here — the call site doesn't carry the dotted package path. If
|
|
1143
|
+
// the same file imports `<module>` via `from <pkg> import <module>`
|
|
1144
|
+
// (recorded in `allRouterModuleAliases`) we promote to a long key.
|
|
1145
|
+
const dotIdx = inc.routerExpr.indexOf('.router');
|
|
1146
|
+
if (dotIdx > 0) {
|
|
1147
|
+
const moduleShort = inc.routerExpr.slice(0, dotIdx);
|
|
1148
|
+
const aliasLong = moduleAliasesByFile.get(inc.filePath)?.get(moduleShort);
|
|
1149
|
+
if (aliasLong) {
|
|
1150
|
+
recordPrefix(prefixesByLongKey, aliasLong, inc.prefix);
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
recordPrefix(prefixesByShortKey, moduleShort, inc.prefix);
|
|
1154
|
+
}
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
// Shape B: bare local name. Resolve through this file's imports. The
|
|
1158
|
+
// import line gives us a long key whenever the module path was multi-
|
|
1159
|
+
// segment, so cross-package collisions are eliminated for Shape B.
|
|
1160
|
+
const localImp = importsByFile.get(inc.filePath)?.get(inc.routerExpr);
|
|
1161
|
+
if (!localImp)
|
|
1162
|
+
continue;
|
|
1163
|
+
if (localImp.moduleKeyLong) {
|
|
1164
|
+
recordPrefix(prefixesByLongKey, localImp.moduleKeyLong, inc.prefix);
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
recordPrefix(prefixesByShortKey, localImp.moduleKey, inc.prefix);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (prefixesByLongKey.size > 0 ||
|
|
1171
|
+
prefixesByShortKey.size > 0 ||
|
|
1172
|
+
allRouterConstructorPrefixes.length > 0) {
|
|
1173
|
+
const fileLongKey = (rel) => {
|
|
1174
|
+
// Strip `.py`, then take the last two path segments. `api/users.py`
|
|
1175
|
+
// → `api/users`. Files at the repo root return the empty string,
|
|
1176
|
+
// which can never match a long-key entry (those always include a
|
|
1177
|
+
// parent directory) and so fall through to the short-key lookup.
|
|
1178
|
+
const noExt = rel.endsWith('.py') ? rel.slice(0, -3) : rel;
|
|
1179
|
+
const lastSlash = noExt.lastIndexOf('/');
|
|
1180
|
+
if (lastSlash < 0)
|
|
1181
|
+
return '';
|
|
1182
|
+
const beforeLast = noExt.slice(0, lastSlash);
|
|
1183
|
+
const stem = noExt.slice(lastSlash + 1);
|
|
1184
|
+
const prevSlash = beforeLast.lastIndexOf('/');
|
|
1185
|
+
const parent = prevSlash >= 0 ? beforeLast.slice(prevSlash + 1) : beforeLast;
|
|
1186
|
+
return `${parent}/${stem}`;
|
|
1187
|
+
};
|
|
1188
|
+
const fileShortKey = (rel) => {
|
|
1189
|
+
const slash = rel.lastIndexOf('/');
|
|
1190
|
+
const file = slash >= 0 ? rel.slice(slash + 1) : rel;
|
|
1191
|
+
return file.endsWith('.py') ? file.slice(0, -3) : file;
|
|
1192
|
+
};
|
|
1193
|
+
for (const ctor of allRouterConstructorPrefixes) {
|
|
1194
|
+
const longKey = fileLongKey(ctor.filePath);
|
|
1195
|
+
if (longKey) {
|
|
1196
|
+
constructorPrefixesByLongKey.set(longKey, ctor.prefix);
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
constructorPrefixesByShortKey.set(fileShortKey(ctor.filePath), ctor.prefix);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const expanded = [];
|
|
1203
|
+
for (const dr of allDecoratorRoutes) {
|
|
1204
|
+
if (dr.decoratorReceiver !== 'router' || !dr.filePath.endsWith('.py')) {
|
|
1205
|
+
expanded.push(dr);
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
// Long-key lookup first; only fall back to the short key when no
|
|
1209
|
+
// long-key prefix targets this file. This avoids prefix leakage
|
|
1210
|
+
// between e.g. `api/users.py` and `admin/users.py`.
|
|
1211
|
+
const longKey = fileLongKey(dr.filePath);
|
|
1212
|
+
const longPrefixes = longKey ? prefixesByLongKey.get(longKey) : undefined;
|
|
1213
|
+
const shortPrefixes = longPrefixes
|
|
1214
|
+
? undefined
|
|
1215
|
+
: prefixesByShortKey.get(fileShortKey(dr.filePath));
|
|
1216
|
+
const prefixes = longPrefixes ?? shortPrefixes;
|
|
1217
|
+
// Constructor prefixes are keyed like include_router prefixes:
|
|
1218
|
+
// long-key entries are precise, while short-key entries are only
|
|
1219
|
+
// valid for repo-root/single-segment files where `fileLongKey`
|
|
1220
|
+
// returns ''. Do not fall back from a missing long-key match to the
|
|
1221
|
+
// short key or a root `users.py` prefix can leak onto
|
|
1222
|
+
// `admin/users.py`.
|
|
1223
|
+
const constructorPrefix = longKey
|
|
1224
|
+
? constructorPrefixesByLongKey.get(longKey)
|
|
1225
|
+
: constructorPrefixesByShortKey.get(fileShortKey(dr.filePath));
|
|
1226
|
+
const routePath = constructorPrefix
|
|
1227
|
+
? (0, route_path_js_1.normalizeExtractedRoutePath)(dr.routePath, constructorPrefix)
|
|
1228
|
+
: dr.routePath;
|
|
1229
|
+
if (!prefixes || prefixes.size === 0) {
|
|
1230
|
+
expanded.push(routePath === dr.routePath ? dr : { ...dr, routePath });
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
for (const prefix of prefixes) {
|
|
1234
|
+
expanded.push({ ...dr, routePath, prefix });
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
allDecoratorRoutes.length = 0;
|
|
1238
|
+
for (const dr of expanded)
|
|
1239
|
+
allDecoratorRoutes.push(dr);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
// Cross-file Spring interface-inheritance pass (#2288): a concrete
|
|
1243
|
+
// `@RestController` inherits the `@*Mapping`s declared on the interfaces it
|
|
1244
|
+
// implements. The per-file `SharedSpringType` views collected by the Java
|
|
1245
|
+
// provider's `extractRouteInheritanceTypes` hook are resolved here, project-
|
|
1246
|
+
// wide, into decorator routes attributed to the implementing controller (the
|
|
1247
|
+
// interface's own per-file routes were suppressed at extraction). Mirrors the
|
|
1248
|
+
// group layer via the shared `resolveInheritedSpringRoutes` so both agree.
|
|
1249
|
+
if (allSpringTypes.length > 0) {
|
|
1250
|
+
for (const inherited of (0, spring_shared_js_1.resolveInheritedSpringRoutes)(allSpringTypes)) {
|
|
1251
|
+
allDecoratorRoutes.push({
|
|
1252
|
+
filePath: inherited.filePath,
|
|
1253
|
+
routePath: inherited.path,
|
|
1254
|
+
httpMethod: inherited.method,
|
|
1255
|
+
decoratorName: 'inherited-mapping',
|
|
1256
|
+
lineNumber: 0,
|
|
1257
|
+
handlerName: inherited.methodName,
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
(0, heap_probe_js_1.logHeapProbe)('parse-impl-return', `exportedTypeMap=${exportedTypeMap.size} parsedFiles=${allParsedFiles.length} nodes=${graph.nodeCount}`);
|
|
1262
|
+
// Part 2 (#2138): resolve each route's handler to a real symbol UID now that
|
|
1263
|
+
// the model is fully populated and decorator-route prefixes are finalized.
|
|
1264
|
+
const routeHandlerSymbols = (0, call_processor_js_1.resolveRouteHandlerSymbols)(model, allExtractedRoutes, allDecoratorRoutes);
|
|
1265
|
+
return {
|
|
1266
|
+
exportedTypeMap,
|
|
1267
|
+
allFetchCalls,
|
|
1268
|
+
allFetchWrapperDefs,
|
|
1269
|
+
allExtractedRoutes,
|
|
1270
|
+
allDecoratorRoutes,
|
|
1271
|
+
allToolDefs,
|
|
1272
|
+
allORMQueries,
|
|
1273
|
+
bindingAccumulator,
|
|
1274
|
+
routeHandlerSymbols,
|
|
1275
|
+
model,
|
|
1276
|
+
// Whether a worker pool was actually constructed for this run. False means
|
|
1277
|
+
// no pool was needed: a warm all-cache-hit run replays cached worker output
|
|
1278
|
+
// without spawning workers, or there were no parseable files.
|
|
1279
|
+
usedWorkerPool: workerPool !== undefined,
|
|
1280
|
+
// Per-file ParsedFile artifacts produced by workers' calls to
|
|
1281
|
+
// `extractParsedFile`. Consumed by scope-resolution as a re-extraction
|
|
1282
|
+
// cache: when the file's ParsedFile is here, scope-resolution skips its own
|
|
1283
|
+
// `extractParsedFile` call.
|
|
1284
|
+
parsedFiles: allParsedFiles,
|
|
1285
|
+
};
|
|
1286
|
+
}
|