gitnexus 1.6.6-rc.99 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -68
- package/dist/_shared/graph/types.d.ts +17 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +3 -5
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +2 -4
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +1 -2
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.js +1 -2
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +24 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +13 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.d.ts +1 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.js +6 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/evidence.d.ts +2 -3
- package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/evidence.js +2 -3
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/macro-registry.d.ts +29 -0
- package/dist/_shared/scope-resolution/registries/macro-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/macro-registry.js +32 -0
- package/dist/_shared/scope-resolution/registries/macro-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +9 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +1 -1
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +35 -1
- package/dist/cli/ai-context.js +111 -12
- package/dist/cli/analyze-config.d.ts +92 -0
- package/dist/cli/analyze-config.js +364 -0
- package/dist/cli/analyze.d.ts +17 -1
- package/dist/cli/analyze.js +224 -71
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +18 -0
- package/dist/cli/doctor.js +32 -0
- package/dist/cli/i18n/en.d.ts +2 -2
- package/dist/cli/i18n/en.js +2 -2
- package/dist/cli/i18n/resources.d.ts +2 -2
- package/dist/cli/i18n/zh-CN.js +2 -2
- package/dist/cli/index.js +4 -2
- package/dist/cli/resolve-invocation.d.ts +21 -0
- package/dist/cli/resolve-invocation.js +68 -0
- package/dist/cli/setup.d.ts +33 -0
- package/dist/cli/setup.js +96 -35
- package/dist/cli/skill-gen.js +2 -2
- package/dist/cli/wiki.js +20 -4
- package/dist/core/embeddings/embedder.d.ts +1 -1
- package/dist/core/embeddings/embedder.js +14 -1
- package/dist/core/embeddings/runtime-support.d.ts +41 -0
- package/dist/core/embeddings/runtime-support.js +65 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +2 -1
- package/dist/core/group/extractors/http-patterns/java.js +15 -1
- package/dist/core/ingestion/call-processor.d.ts +57 -227
- package/dist/core/ingestion/call-processor.js +172 -2595
- package/dist/core/ingestion/call-routing.d.ts +6 -5
- package/dist/core/ingestion/call-routing.js +6 -6
- package/dist/core/ingestion/call-types.d.ts +0 -75
- package/dist/core/ingestion/class-extractors/configs/c-cpp.js +26 -1
- package/dist/core/ingestion/class-extractors/configs/ruby.js +3 -0
- package/dist/core/ingestion/class-extractors/generic.js +9 -11
- package/dist/core/ingestion/class-types.d.ts +23 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +11 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +227 -73
- package/dist/core/ingestion/cobol-processor.d.ts +1 -0
- package/dist/core/ingestion/cobol-processor.js +51 -2
- package/dist/core/ingestion/emit-references.js +8 -2
- package/dist/core/ingestion/export-detection.d.ts +2 -1
- package/dist/core/ingestion/export-detection.js +2 -6
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +0 -6
- package/dist/core/ingestion/field-extractors/configs/dart.js +62 -44
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +0 -9
- package/dist/core/ingestion/field-extractors/configs/go.js +40 -1
- package/dist/core/ingestion/field-extractors/configs/jvm.js +43 -4
- package/dist/core/ingestion/field-extractors/configs/php.js +1 -2
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +12 -1
- package/dist/core/ingestion/field-extractors/configs/swift.js +31 -6
- package/dist/core/ingestion/field-extractors/generic.d.ts +6 -0
- package/dist/core/ingestion/field-extractors/generic.js +5 -4
- package/dist/core/ingestion/field-extractors/typescript.js +0 -29
- package/dist/core/ingestion/filesystem-walker.d.ts +0 -9
- package/dist/core/ingestion/filesystem-walker.js +0 -11
- package/dist/core/ingestion/finalize-orchestrator.js +14 -2
- package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +21 -0
- package/dist/core/ingestion/import-resolvers/configs/swift.js +72 -11
- package/dist/core/ingestion/import-resolvers/python.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/standard.d.ts +2 -2
- package/dist/core/ingestion/import-resolvers/standard.js +6 -5
- package/dist/core/ingestion/import-resolvers/utils.d.ts +2 -5
- package/dist/core/ingestion/import-resolvers/utils.js +1 -9
- package/dist/core/ingestion/language-provider.d.ts +39 -119
- package/dist/core/ingestion/language-provider.js +0 -2
- package/dist/core/ingestion/languages/c/capture-side-channel.d.ts +62 -0
- package/dist/core/ingestion/languages/c/capture-side-channel.js +65 -0
- package/dist/core/ingestion/languages/c/captures.js +41 -23
- package/dist/core/ingestion/languages/c/import-decomposer.d.ts +9 -0
- package/dist/core/ingestion/languages/c/import-decomposer.js +15 -0
- package/dist/core/ingestion/languages/c/import-target.js +52 -9
- package/dist/core/ingestion/languages/c/index.d.ts +2 -1
- package/dist/core/ingestion/languages/c/index.js +2 -1
- package/dist/core/ingestion/languages/c/query.js +15 -0
- package/dist/core/ingestion/languages/c/scope-resolver.js +47 -4
- package/dist/core/ingestion/languages/c/static-linkage.d.ts +8 -0
- package/dist/core/ingestion/languages/c/static-linkage.js +46 -1
- package/dist/core/ingestion/languages/c-cpp.js +16 -7
- package/dist/core/ingestion/languages/cobol/captures.js +25 -2
- package/dist/core/ingestion/languages/cpp/adl.d.ts +88 -4
- package/dist/core/ingestion/languages/cpp/adl.js +380 -160
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +2 -8
- package/dist/core/ingestion/languages/cpp/capture-side-channel.d.ts +65 -0
- package/dist/core/ingestion/languages/cpp/capture-side-channel.js +78 -0
- package/dist/core/ingestion/languages/cpp/captures.js +162 -54
- package/dist/core/ingestion/languages/cpp/file-local-linkage.d.ts +18 -52
- package/dist/core/ingestion/languages/cpp/file-local-linkage.js +49 -1
- package/dist/core/ingestion/languages/cpp/import-decomposer.d.ts +7 -0
- package/dist/core/ingestion/languages/cpp/import-decomposer.js +12 -0
- package/dist/core/ingestion/languages/cpp/index.d.ts +1 -0
- package/dist/core/ingestion/languages/cpp/index.js +1 -0
- package/dist/core/ingestion/languages/cpp/inline-namespaces.d.ts +8 -0
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +21 -0
- package/dist/core/ingestion/languages/cpp/query.js +21 -0
- package/dist/core/ingestion/languages/cpp/range-bindings.js +20 -5
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +52 -5
- package/dist/core/ingestion/languages/cpp/two-phase-lookup.d.ts +20 -0
- package/dist/core/ingestion/languages/cpp/two-phase-lookup.js +83 -3
- package/dist/core/ingestion/languages/csharp/captures.js +205 -41
- package/dist/core/ingestion/languages/csharp/index.d.ts +3 -4
- package/dist/core/ingestion/languages/csharp/index.js +3 -4
- package/dist/core/ingestion/languages/csharp/interpret.js +17 -3
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +17 -7
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +129 -124
- package/dist/core/ingestion/languages/csharp/qualified-type-names.d.ts +14 -0
- package/dist/core/ingestion/languages/csharp/qualified-type-names.js +59 -0
- package/dist/core/ingestion/languages/csharp/query.js +13 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +10 -6
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -1
- package/dist/core/ingestion/languages/csharp.js +0 -5
- package/dist/core/ingestion/languages/dart/arity-metadata.d.ts +25 -0
- package/dist/core/ingestion/languages/dart/arity-metadata.js +35 -0
- package/dist/core/ingestion/languages/dart/arity.d.ts +19 -0
- package/dist/core/ingestion/languages/dart/arity.js +31 -0
- package/dist/core/ingestion/languages/dart/built-ins.d.ts +11 -0
- package/dist/core/ingestion/languages/dart/built-ins.js +34 -0
- package/dist/core/ingestion/languages/dart/cache-stats.d.ts +13 -0
- package/dist/core/ingestion/languages/dart/cache-stats.js +24 -0
- package/dist/core/ingestion/languages/dart/captures.d.ts +29 -0
- package/dist/core/ingestion/languages/dart/captures.js +445 -0
- package/dist/core/ingestion/languages/dart/expand-wildcards.d.ts +13 -0
- package/dist/core/ingestion/languages/dart/expand-wildcards.js +31 -0
- package/dist/core/ingestion/languages/dart/import-target.d.ts +15 -0
- package/dist/core/ingestion/languages/dart/import-target.js +65 -0
- package/dist/core/ingestion/languages/dart/index.d.ts +28 -0
- package/dist/core/ingestion/languages/dart/index.js +28 -0
- package/dist/core/ingestion/languages/dart/interpret.d.ts +24 -0
- package/dist/core/ingestion/languages/dart/interpret.js +95 -0
- package/dist/core/ingestion/languages/dart/merge-bindings.d.ts +10 -0
- package/dist/core/ingestion/languages/dart/merge-bindings.js +39 -0
- package/dist/core/ingestion/languages/dart/query.d.ts +26 -0
- package/dist/core/ingestion/languages/dart/query.js +142 -0
- package/dist/core/ingestion/languages/dart/receiver-binding.d.ts +24 -0
- package/dist/core/ingestion/languages/dart/receiver-binding.js +87 -0
- package/dist/core/ingestion/languages/dart/scope-resolver.d.ts +29 -0
- package/dist/core/ingestion/languages/dart/scope-resolver.js +194 -0
- package/dist/core/ingestion/languages/dart/signature-bindings.d.ts +23 -0
- package/dist/core/ingestion/languages/dart/signature-bindings.js +51 -0
- package/dist/core/ingestion/languages/dart/simple-hooks.d.ts +20 -0
- package/dist/core/ingestion/languages/dart/simple-hooks.js +47 -0
- package/dist/core/ingestion/languages/dart.d.ts +0 -1
- package/dist/core/ingestion/languages/dart.js +15 -29
- package/dist/core/ingestion/languages/go/arity-metadata.d.ts +1 -0
- package/dist/core/ingestion/languages/go/arity-metadata.js +31 -1
- package/dist/core/ingestion/languages/go/captures.js +162 -3
- package/dist/core/ingestion/languages/go/interface-impls.js +476 -50
- package/dist/core/ingestion/languages/go/interpret.js +21 -11
- package/dist/core/ingestion/languages/go/method-owners.js +20 -8
- package/dist/core/ingestion/languages/go/query.js +15 -7
- package/dist/core/ingestion/languages/go/range-binding.js +24 -9
- package/dist/core/ingestion/languages/go/receiver-binding.js +1 -1
- package/dist/core/ingestion/languages/go/scope-resolver.js +2 -2
- package/dist/core/ingestion/languages/go/simple-hooks.js +10 -5
- package/dist/core/ingestion/languages/go/type-binding.d.ts +1 -0
- package/dist/core/ingestion/languages/go/type-binding.js +40 -2
- package/dist/core/ingestion/languages/go.d.ts +0 -1
- package/dist/core/ingestion/languages/go.js +0 -5
- package/dist/core/ingestion/languages/index.d.ts +0 -5
- package/dist/core/ingestion/languages/index.js +0 -3
- package/dist/core/ingestion/languages/java/captures.js +252 -23
- package/dist/core/ingestion/languages/java/interpret.js +12 -6
- package/dist/core/ingestion/languages/java/package-siblings.js +18 -5
- package/dist/core/ingestion/languages/java/query.js +16 -1
- package/dist/core/ingestion/languages/java/scope-resolver.d.ts +2 -8
- package/dist/core/ingestion/languages/java/scope-resolver.js +2 -8
- package/dist/core/ingestion/languages/java.js +0 -5
- package/dist/core/ingestion/languages/javascript/captures.js +185 -11
- package/dist/core/ingestion/languages/javascript/query.js +107 -1
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +57 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +60 -0
- package/dist/core/ingestion/languages/kotlin/captures.js +213 -22
- package/dist/core/ingestion/languages/kotlin/companion-scopes.d.ts +7 -0
- package/dist/core/ingestion/languages/kotlin/companion-scopes.js +10 -0
- package/dist/core/ingestion/languages/kotlin/index.d.ts +1 -0
- package/dist/core/ingestion/languages/kotlin/index.js +1 -0
- package/dist/core/ingestion/languages/kotlin/query.js +30 -0
- package/dist/core/ingestion/languages/kotlin/scope-resolver.d.ts +5 -7
- package/dist/core/ingestion/languages/kotlin/scope-resolver.js +22 -7
- package/dist/core/ingestion/languages/kotlin.js +8 -5
- package/dist/core/ingestion/languages/php/captures.js +135 -19
- package/dist/core/ingestion/languages/php/import-decomposer.js +21 -63
- package/dist/core/ingestion/languages/php/index.d.ts +3 -4
- package/dist/core/ingestion/languages/php/index.js +3 -4
- package/dist/core/ingestion/languages/php/query.js +5 -0
- package/dist/core/ingestion/languages/php/receiver-binding.js +8 -4
- package/dist/core/ingestion/languages/php.js +0 -4
- package/dist/core/ingestion/languages/python/captures.js +128 -13
- package/dist/core/ingestion/languages/python/depends-references.js +1 -1
- package/dist/core/ingestion/languages/python/import-target.d.ts +4 -5
- package/dist/core/ingestion/languages/python/import-target.js +108 -16
- package/dist/core/ingestion/languages/python/index-stats.d.ts +18 -0
- package/dist/core/ingestion/languages/python/index-stats.js +25 -0
- package/dist/core/ingestion/languages/python/index.d.ts +3 -4
- package/dist/core/ingestion/languages/python/index.js +3 -4
- package/dist/core/ingestion/languages/python/query.js +17 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +7 -6
- package/dist/core/ingestion/languages/python.d.ts +0 -2
- package/dist/core/ingestion/languages/python.js +0 -7
- package/dist/core/ingestion/languages/ruby/captures.js +174 -31
- package/dist/core/ingestion/languages/ruby/import-target.js +2 -1
- package/dist/core/ingestion/languages/ruby/interpret.js +2 -1
- package/dist/core/ingestion/languages/ruby/query.js +10 -0
- package/dist/core/ingestion/languages/ruby/scope-resolver.js +61 -11
- package/dist/core/ingestion/languages/ruby.js +4 -53
- package/dist/core/ingestion/languages/rust/captures.js +95 -13
- package/dist/core/ingestion/languages/rust/query.js +64 -1
- package/dist/core/ingestion/languages/rust/range-binding.js +40 -12
- package/dist/core/ingestion/languages/rust/receiver-binding.js +9 -0
- package/dist/core/ingestion/languages/rust/scope-resolver.js +75 -1
- package/dist/core/ingestion/languages/rust.d.ts +0 -2
- package/dist/core/ingestion/languages/rust.js +0 -6
- package/dist/core/ingestion/languages/swift/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/swift/arity-metadata.js +41 -0
- package/dist/core/ingestion/languages/swift/arity.d.ts +29 -0
- package/dist/core/ingestion/languages/swift/arity.js +42 -0
- package/dist/core/ingestion/languages/swift/base-type.d.ts +20 -0
- package/dist/core/ingestion/languages/swift/base-type.js +27 -0
- package/dist/core/ingestion/languages/swift/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/swift/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/swift/captures.d.ts +35 -0
- package/dist/core/ingestion/languages/swift/captures.js +495 -0
- package/dist/core/ingestion/languages/swift/implicit-imports.d.ts +34 -0
- package/dist/core/ingestion/languages/swift/implicit-imports.js +57 -0
- package/dist/core/ingestion/languages/swift/import-decomposer.d.ts +24 -0
- package/dist/core/ingestion/languages/swift/import-decomposer.js +84 -0
- package/dist/core/ingestion/languages/swift/import-target.d.ts +33 -0
- package/dist/core/ingestion/languages/swift/import-target.js +81 -0
- package/dist/core/ingestion/languages/swift/index.d.ts +37 -0
- package/dist/core/ingestion/languages/swift/index.js +37 -0
- package/dist/core/ingestion/languages/swift/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/swift/interpret.js +89 -0
- package/dist/core/ingestion/languages/swift/merge-bindings.d.ts +20 -0
- package/dist/core/ingestion/languages/swift/merge-bindings.js +48 -0
- package/dist/core/ingestion/languages/swift/query.d.ts +46 -0
- package/dist/core/ingestion/languages/swift/query.js +194 -0
- package/dist/core/ingestion/languages/swift/receiver-binding.d.ts +40 -0
- package/dist/core/ingestion/languages/swift/receiver-binding.js +161 -0
- package/dist/core/ingestion/languages/swift/scope-resolver.d.ts +50 -0
- package/dist/core/ingestion/languages/swift/scope-resolver.js +189 -0
- package/dist/core/ingestion/languages/swift/sibling-type-bindings.d.ts +34 -0
- package/dist/core/ingestion/languages/swift/sibling-type-bindings.js +65 -0
- package/dist/core/ingestion/languages/swift/signature-bindings.d.ts +25 -0
- package/dist/core/ingestion/languages/swift/signature-bindings.js +66 -0
- package/dist/core/ingestion/languages/swift/simple-hooks.d.ts +37 -0
- package/dist/core/ingestion/languages/swift/simple-hooks.js +60 -0
- package/dist/core/ingestion/languages/swift/target-grouping.d.ts +51 -0
- package/dist/core/ingestion/languages/swift/target-grouping.js +93 -0
- package/dist/core/ingestion/languages/swift/target-siblings.d.ts +30 -0
- package/dist/core/ingestion/languages/swift/target-siblings.js +71 -0
- package/dist/core/ingestion/languages/swift.d.ts +0 -5
- package/dist/core/ingestion/languages/swift.js +11 -87
- package/dist/core/ingestion/languages/typescript/array-callback.d.ts +3 -29
- package/dist/core/ingestion/languages/typescript/array-callback.js +4 -43
- package/dist/core/ingestion/languages/typescript/captures.js +147 -17
- package/dist/core/ingestion/languages/typescript/index.d.ts +3 -4
- package/dist/core/ingestion/languages/typescript/index.js +3 -4
- package/dist/core/ingestion/languages/typescript/query.d.ts +2 -3
- package/dist/core/ingestion/languages/typescript/query.js +125 -4
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +0 -1
- package/dist/core/ingestion/languages/typescript.d.ts +2 -3
- package/dist/core/ingestion/languages/typescript.js +46 -23
- package/dist/core/ingestion/languages/vue/captures.d.ts +46 -0
- package/dist/core/ingestion/languages/vue/captures.js +67 -0
- package/dist/core/ingestion/languages/vue/import-target.d.ts +27 -0
- package/dist/core/ingestion/languages/vue/import-target.js +58 -0
- package/dist/core/ingestion/languages/vue/index.d.ts +49 -0
- package/dist/core/ingestion/languages/vue/index.js +49 -0
- package/dist/core/ingestion/languages/vue/scope-resolver.d.ts +55 -0
- package/dist/core/ingestion/languages/vue/scope-resolver.js +292 -0
- package/dist/core/ingestion/languages/vue.js +12 -4
- package/dist/core/ingestion/local-symbol-pruner.d.ts +11 -0
- package/dist/core/ingestion/local-symbol-pruner.js +63 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +14 -1
- package/dist/core/ingestion/method-extractors/configs/jvm.js +37 -20
- package/dist/core/ingestion/method-extractors/configs/php.js +0 -1
- package/dist/core/ingestion/method-extractors/configs/swift.js +11 -4
- package/dist/core/ingestion/method-types.d.ts +2 -2
- package/dist/core/ingestion/model/index.d.ts +0 -3
- package/dist/core/ingestion/model/index.js +0 -8
- package/dist/core/ingestion/model/resolve.d.ts +7 -43
- package/dist/core/ingestion/model/resolve.js +7 -226
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +46 -1
- package/dist/core/ingestion/model/semantic-model.d.ts +4 -6
- package/dist/core/ingestion/model/semantic-model.js +1 -1
- package/dist/core/ingestion/model/symbol-table.d.ts +3 -4
- package/dist/core/ingestion/model/symbol-table.js +3 -4
- package/dist/core/ingestion/parsing-processor.d.ts +30 -24
- package/dist/core/ingestion/parsing-processor.js +76 -640
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +1 -1
- package/dist/core/ingestion/pipeline-phases/communities.js +5 -2
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +9 -8
- package/dist/core/ingestion/pipeline-phases/cross-file.js +16 -12
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +1 -1
- package/dist/core/ingestion/pipeline-phases/mro.js +2 -2
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +44 -22
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +426 -565
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +13 -33
- package/dist/core/ingestion/pipeline-phases/parse.js +2 -2
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +1 -1
- package/dist/core/ingestion/pipeline-phases/processes.js +5 -3
- package/dist/core/ingestion/pipeline-phases/prune-local-symbols.d.ts +14 -0
- package/dist/core/ingestion/pipeline-phases/prune-local-symbols.js +26 -0
- package/dist/core/ingestion/pipeline.d.ts +27 -19
- package/dist/core/ingestion/pipeline.js +4 -2
- package/dist/core/ingestion/resolve-references.js +15 -3
- package/dist/core/ingestion/route-extractors/laravel.d.ts +10 -0
- package/dist/core/ingestion/route-extractors/laravel.js +88 -9
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +2 -1
- package/dist/core/ingestion/scope-extractor-bridge.js +2 -2
- package/dist/core/ingestion/scope-extractor.js +13 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +181 -15
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +21 -10
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +10 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +7 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +58 -6
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +24 -5
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +68 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +189 -29
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +21 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1 -1
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +8 -11
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +171 -50
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +3 -4
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +9 -4
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +28 -5
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +191 -24
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +31 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +72 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +440 -39
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +18 -2
- package/dist/core/ingestion/scope-resolution/workspace-index.js +94 -7
- package/dist/core/ingestion/tree-sitter-queries.d.ts +14 -14
- package/dist/core/ingestion/tree-sitter-queries.js +380 -158
- package/dist/core/ingestion/ts-js-hoc-utils.d.ts +12 -0
- package/dist/core/ingestion/ts-js-hoc-utils.js +105 -0
- package/dist/core/ingestion/type-env.d.ts +2 -1
- package/dist/core/ingestion/type-env.js +6 -8
- package/dist/core/ingestion/type-extractors/c-cpp.js +8 -10
- package/dist/core/ingestion/type-extractors/csharp.js +18 -34
- package/dist/core/ingestion/type-extractors/go.js +2 -10
- package/dist/core/ingestion/type-extractors/jvm.js +6 -5
- package/dist/core/ingestion/type-extractors/php.js +1 -1
- package/dist/core/ingestion/type-extractors/python.js +1 -1
- package/dist/core/ingestion/type-extractors/ruby.js +2 -2
- package/dist/core/ingestion/type-extractors/rust.js +0 -16
- package/dist/core/ingestion/type-extractors/shared.d.ts +2 -2
- package/dist/core/ingestion/type-extractors/shared.js +25 -11
- package/dist/core/ingestion/type-extractors/swift.js +5 -5
- package/dist/core/ingestion/type-extractors/types.d.ts +1 -1
- package/dist/core/ingestion/type-extractors/typescript.js +1 -1
- package/dist/core/ingestion/utils/ast-helpers.d.ts +119 -1
- package/dist/core/ingestion/utils/ast-helpers.js +260 -13
- package/dist/core/ingestion/utils/callable-labels.d.ts +13 -0
- package/dist/core/ingestion/utils/callable-labels.js +14 -0
- package/dist/core/ingestion/utils/deferred-resolution-profile.d.ts +10 -0
- package/dist/core/ingestion/utils/deferred-resolution-profile.js +30 -0
- package/dist/core/ingestion/utils/heap-probe.d.ts +20 -0
- package/dist/core/ingestion/utils/heap-probe.js +39 -0
- package/dist/core/ingestion/utils/heritage-marker.d.ts +33 -0
- package/dist/core/ingestion/utils/heritage-marker.js +41 -0
- package/dist/core/ingestion/utils/qualified-name.d.ts +37 -0
- package/dist/core/ingestion/utils/qualified-name.js +67 -0
- package/dist/core/ingestion/variable-extractors/configs/c-cpp.js +49 -0
- package/dist/core/ingestion/variable-extractors/configs/dart.js +76 -62
- package/dist/core/ingestion/variable-extractors/configs/go.js +74 -7
- package/dist/core/ingestion/variable-extractors/configs/jvm.js +35 -8
- package/dist/core/ingestion/variable-extractors/generic.js +40 -12
- package/dist/core/ingestion/variable-types.d.ts +9 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +97 -7
- package/dist/core/ingestion/vue-sfc-extractor.js +433 -11
- package/dist/core/ingestion/workers/parse-worker.d.ts +0 -11
- package/dist/core/ingestion/workers/parse-worker.js +358 -187
- package/dist/core/ingestion/workers/worker-pool.d.ts +78 -9
- package/dist/core/ingestion/workers/worker-pool.js +346 -49
- package/dist/core/lbug/csv-generator.js +20 -2
- package/dist/core/lbug/lbug-adapter.js +29 -10
- package/dist/core/lbug/native-check.js +17 -5
- package/dist/core/lbug/pool-adapter.d.ts +0 -19
- package/dist/core/lbug/pool-adapter.js +40 -77
- package/dist/core/lbug/query-result-utils.d.ts +19 -0
- package/dist/core/lbug/query-result-utils.js +28 -0
- package/dist/core/run-analyze.d.ts +9 -2
- package/dist/core/run-analyze.js +24 -3
- package/dist/core/tree-sitter/parser-loader.d.ts +13 -0
- package/dist/core/tree-sitter/parser-loader.js +4 -0
- package/dist/core/tree-sitter/safe-parse.d.ts +70 -3
- package/dist/core/tree-sitter/safe-parse.js +214 -11
- package/dist/core/wiki/generator.js +13 -5
- package/dist/core/wiki/graph-queries.d.ts +3 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/core/wiki/llm-client.d.ts +1 -1
- package/dist/core/wiki/llm-client.js +7 -2
- package/dist/core/wiki/local-cli-client.d.ts +2 -1
- package/dist/core/wiki/local-cli-client.js +62 -4
- package/dist/mcp/compatible-stdio-transport.d.ts +3 -0
- package/dist/mcp/compatible-stdio-transport.js +22 -0
- package/dist/mcp/core/embedder.d.ts +1 -1
- package/dist/mcp/core/embedder.js +13 -1
- package/dist/mcp/local/local-backend.d.ts +37 -5
- package/dist/mcp/local/local-backend.js +227 -129
- package/dist/mcp/resources.js +3 -3
- package/dist/mcp/server.js +9 -1
- package/dist/storage/git.d.ts +11 -0
- package/dist/storage/git.js +30 -0
- package/dist/storage/parse-cache.d.ts +23 -1
- package/dist/storage/parse-cache.js +138 -32
- package/dist/storage/parsedfile-store.d.ts +142 -0
- package/dist/storage/parsedfile-store.js +367 -0
- package/dist/storage/repo-manager.d.ts +2 -1
- package/dist/storage/scope-index-store.d.ts +114 -0
- package/dist/storage/scope-index-store.js +236 -0
- package/dist/types/pipeline.d.ts +6 -3
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +2 -1
- package/hooks/claude/gitnexus-hook.cjs +2 -1
- package/hooks/claude/resolve-analyze-cmd.cjs +323 -0
- package/package.json +4 -5
- package/scripts/bench-scope-resolution.ts +1 -3
- package/scripts/cross-platform-tests.ts +4 -1
- package/skills/gitnexus-cli.md +8 -6
- package/skills/gitnexus-debugging.md +15 -15
- package/skills/gitnexus-exploring.md +11 -11
- package/skills/gitnexus-guide.md +1 -1
- package/skills/gitnexus-impact-analysis.md +10 -10
- package/skills/gitnexus-pr-review.md +19 -19
- package/skills/gitnexus-refactoring.md +25 -25
- package/web/assets/{agent-BRX-afm3.js → agent-EkX7webX.js} +156 -131
- package/web/assets/{architectureDiagram-UL44E2DR-B1oYB8-c.js → architectureDiagram-UL44E2DR-BoczH8cv.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-SPC6pgWI.js → chunk-LCXTWHL2-By5HB3b-.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-DZXr4Yv3.js → chunk-RG4AUYOV-BQE_DkpL.js} +1 -1
- package/web/assets/{classDiagram-KGZ6W3CR-BqpOj8L9.js → classDiagram-KGZ6W3CR-Dj_02GUH.js} +1 -1
- package/web/assets/{classDiagram-v2-72OJOZXJ-FmElywK2.js → classDiagram-v2-72OJOZXJ-D-NArMpf.js} +1 -1
- package/web/assets/{diagram-3NCE3AQN-BWtJXMDq.js → diagram-3NCE3AQN-VXtXDO95.js} +1 -1
- package/web/assets/{diagram-GF46GFSD-Hh-fChxU.js → diagram-GF46GFSD-B1qab2Ah.js} +1 -1
- package/web/assets/{diagram-QXG6HAR7-DYeQHxGU.js → diagram-QXG6HAR7-BSCHGzzT.js} +1 -1
- package/web/assets/{diagram-WEQXMOUZ-DYGU4T0y.js → diagram-WEQXMOUZ-DHtknmNT.js} +1 -1
- package/web/assets/{erDiagram-L5TCEMPS-bN3wlP2A.js → erDiagram-L5TCEMPS-Db-IYTmm.js} +1 -1
- package/web/assets/{flowDiagram-H6V6AXG4-YACVtF3-.js → flowDiagram-H6V6AXG4-BHDoyVoW.js} +1 -1
- package/web/assets/{index-BhnAdWTt.js → index-BR0IhgbR.js} +10 -10
- package/web/assets/{infoDiagram-3YFTVSEB-BWBzD28-.js → infoDiagram-3YFTVSEB-6h8wbWEe.js} +1 -1
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-B8qYT-j9.js → ishikawaDiagram-BNXS4ZKH-Dx2OkPk1.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-BeoPr5Tl.js → kanban-definition-75IXJCU3-CIIEZfqV.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CdLLN_so.js → mindmap-definition-2TDM6QVE-DUAqydRq.js} +1 -1
- package/web/assets/{pieDiagram-CU6KROY3-DEd2a_k_.js → pieDiagram-CU6KROY3-BntYuYZa.js} +1 -1
- package/web/assets/{requirementDiagram-JXO7QTGE-QNeTo5C_.js → requirementDiagram-JXO7QTGE-QCD6I29n.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-BHC7q7yJ.js → sequenceDiagram-VS2MUI6T-DScFwLz2.js} +1 -1
- package/web/assets/{stateDiagram-7D4R322I-KONnc2Px.js → stateDiagram-7D4R322I-AkRAm6Hl.js} +1 -1
- package/web/assets/{stateDiagram-v2-36443NZ5-C2QfuMlu.js → stateDiagram-v2-36443NZ5-BCK1vigR.js} +1 -1
- package/web/assets/{timeline-definition-O6YCAMPW-D4t4NJx1.js → timeline-definition-O6YCAMPW-BvpIVXjX.js} +1 -1
- package/web/assets/{vennDiagram-MWXL3ELB-6bctC9Yg.js → vennDiagram-MWXL3ELB-BYGWk6dK.js} +1 -1
- package/web/assets/{wardleyDiagram-CUQ6CDDI-BPZYNTvC.js → wardleyDiagram-CUQ6CDDI-Dk1z7H2T.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-lBI9O6DW.js → xychartDiagram-N2JHSOCM-2N6NCSWN.js} +1 -1
- package/web/index.html +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +0 -63
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +0 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.js +0 -122
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +0 -1
- package/dist/_shared/scope-resolution/shadow/diff.d.ts +0 -59
- package/dist/_shared/scope-resolution/shadow/diff.d.ts.map +0 -1
- package/dist/_shared/scope-resolution/shadow/diff.js +0 -79
- package/dist/_shared/scope-resolution/shadow/diff.js.map +0 -1
- package/dist/core/ingestion/ast-cache.d.ts +0 -26
- package/dist/core/ingestion/ast-cache.js +0 -48
- package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +0 -13
- package/dist/core/ingestion/heritage-extractors/configs/go.js +0 -20
- package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +0 -18
- package/dist/core/ingestion/heritage-extractors/configs/ruby.js +0 -65
- package/dist/core/ingestion/heritage-extractors/generic.d.ts +0 -23
- package/dist/core/ingestion/heritage-extractors/generic.js +0 -47
- package/dist/core/ingestion/heritage-processor.d.ts +0 -54
- package/dist/core/ingestion/heritage-processor.js +0 -367
- package/dist/core/ingestion/heritage-types.d.ts +0 -73
- package/dist/core/ingestion/heritage-types.js +0 -2
- package/dist/core/ingestion/import-processor.d.ts +0 -23
- package/dist/core/ingestion/import-processor.js +0 -377
- package/dist/core/ingestion/model/heritage-map.d.ts +0 -105
- package/dist/core/ingestion/model/heritage-map.js +0 -294
- package/dist/core/ingestion/model/resolution-context.d.ts +0 -93
- package/dist/core/ingestion/model/resolution-context.js +0 -337
- package/dist/core/ingestion/named-bindings/csharp.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/csharp.js +0 -37
- package/dist/core/ingestion/named-bindings/java.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/java.js +0 -29
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/kotlin.js +0 -36
- package/dist/core/ingestion/named-bindings/php.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/php.js +0 -61
- package/dist/core/ingestion/named-bindings/python.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/python.js +0 -49
- package/dist/core/ingestion/named-bindings/rust.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/rust.js +0 -66
- package/dist/core/ingestion/named-bindings/types.d.ts +0 -16
- package/dist/core/ingestion/named-bindings/types.js +0 -6
- package/dist/core/ingestion/named-bindings/typescript.d.ts +0 -3
- package/dist/core/ingestion/named-bindings/typescript.js +0 -58
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +0 -17
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +0 -209
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +0 -22
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +0 -92
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +0 -70
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +0 -312
- package/dist/core/ingestion/registry-primary-flag.d.ts +0 -89
- package/dist/core/ingestion/registry-primary-flag.js +0 -122
- package/dist/core/ingestion/shadow-harness.d.ts +0 -113
- package/dist/core/ingestion/shadow-harness.js +0 -148
- package/dist/core/ingestion/utils/ruby-self-call.d.ts +0 -52
- package/dist/core/ingestion/utils/ruby-self-call.js +0 -59
- package/scripts/ci-list-migrated-languages.ts +0 -24
- package/scripts/run-parity.ts +0 -128
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* Parse implementation — chunked parse + resolve loop.
|
|
3
3
|
*
|
|
4
4
|
* This is the core parsing engine of the ingestion pipeline. It reads
|
|
5
|
-
* source files in byte-budget chunks (~20MB each), parses via worker
|
|
6
|
-
* pool (
|
|
7
|
-
*
|
|
5
|
+
* source files in byte-budget chunks (~20MB each), parses via the worker
|
|
6
|
+
* pool (the sole parse path — there is no sequential fallback), and emits
|
|
7
|
+
* route CALLS edges. Import,
|
|
8
|
+
* call, and inheritance resolution are owned by the scope-resolution
|
|
9
|
+
* phase, not here (RING4-1 #942 removed the legacy call DAG; RING4-2 #943
|
|
10
|
+
* removed the legacy per-file import resolution + wildcard synthesis).
|
|
8
11
|
*
|
|
9
12
|
* Consumed by the parse phase (`parse.ts`) — the phase file handles
|
|
10
13
|
* dependency wiring while the heavy implementation lives here.
|
|
@@ -12,29 +15,22 @@
|
|
|
12
15
|
* @module
|
|
13
16
|
*/
|
|
14
17
|
import { BindingAccumulator, enrichExportedTypeMap, } from '../binding-accumulator.js';
|
|
15
|
-
import {
|
|
16
|
-
import { fileContentHash, computeChunkHash } from '../../../storage/parse-cache.js';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { buildHeritageMap } from '../model/heritage-map.js';
|
|
21
|
-
import { processHeritage, processHeritageFromExtracted, extractExtractedHeritageFromFiles, getHeritageStrategyForLanguage, } from '../heritage-processor.js';
|
|
22
|
-
import { createResolutionContext } from '../model/resolution-context.js';
|
|
23
|
-
import { createASTCache } from '../ast-cache.js';
|
|
18
|
+
import { mergeChunkResults, dispatchChunkParse } from '../parsing-processor.js';
|
|
19
|
+
import { fileContentHash, computeChunkHash, loadParseCacheChunk, persistParseCacheChunk, PARSE_CACHE_VERSION, } from '../../../storage/parse-cache.js';
|
|
20
|
+
import { clearParsedFileStore, persistParsedFileChunk, getDurableParsedFileDir, loadDurableParsedFileIndex, restoreDurableParsedFileShard, } from '../../../storage/parsedfile-store.js';
|
|
21
|
+
import { processRoutesFromExtracted, buildExportedTypeMapFromGraph, } from '../call-processor.js';
|
|
22
|
+
import { createSemanticModel } from '../model/index.js';
|
|
24
23
|
import { getLanguageFromFilename } from '../../../_shared/index.js';
|
|
25
|
-
import { isRegistryPrimary } from '../registry-primary-flag.js';
|
|
26
24
|
import { readFileContents } from '../filesystem-walker.js';
|
|
27
25
|
import { isLanguageAvailable } from '../../tree-sitter/parser-loader.js';
|
|
28
|
-
import { createWorkerPool, WorkerPoolInitializationError } from '../workers/worker-pool.js';
|
|
29
|
-
import { extractFetchCallsFromFiles } from '../call-processor.js';
|
|
26
|
+
import { createWorkerPool, workerPoolDisabledByEnv, resolveAutoPoolSize, WorkerPoolInitializationError, WorkerPoolDisabledError, } from '../workers/worker-pool.js';
|
|
30
27
|
import fs from 'node:fs';
|
|
31
28
|
import path from 'node:path';
|
|
32
29
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
33
30
|
import { isDev } from '../utils/env.js';
|
|
34
31
|
import { isVerboseIngestionEnabled } from '../utils/verbose.js';
|
|
35
32
|
import { endTimer, isDeferredResolutionProfileEnabled, logDeferredProfile, startTimer, } from '../utils/deferred-resolution-profile.js';
|
|
36
|
-
import {
|
|
37
|
-
import { extractORMQueriesInline } from './orm-extraction.js';
|
|
33
|
+
import { isDebugHeapEnabled, logHeapProbe } from '../utils/heap-probe.js';
|
|
38
34
|
import { logger } from '../../logger.js';
|
|
39
35
|
// ── Constants ──────────────────────────────────────────────────────────────
|
|
40
36
|
/** Max bytes of source content to load per parse chunk.
|
|
@@ -57,29 +53,107 @@ import { logger } from '../../logger.js';
|
|
|
57
53
|
* import time and meant per-call option threading silently no-op'd.
|
|
58
54
|
*/
|
|
59
55
|
const DEFAULT_CHUNK_BYTE_BUDGET = 2 * 1024 * 1024;
|
|
60
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Per-worker share of a chunk's byte budget when auto-scaling (#worker-idle).
|
|
58
|
+
*
|
|
59
|
+
* A chunk is a single `WorkerPool.dispatch` unit; the pool fans a chunk's files
|
|
60
|
+
* into sub-batch jobs and assigns them to idle workers (`wakeIdleSlots`). When
|
|
61
|
+
* the chunk budget (2 MB) was far below the 8 MB sub-batch cap, every chunk
|
|
62
|
+
* produced exactly ONE job → ONE busy worker while the other N-1 sat idle. To
|
|
63
|
+
* keep all workers fed, the auto chunk budget now scales as
|
|
64
|
+
* `poolSize × CHUNK_BYTES_PER_WORKER`, so each dispatch carries enough work to
|
|
65
|
+
* fan across the whole pool. Sequential / explicit-budget runs are unaffected.
|
|
66
|
+
*/
|
|
67
|
+
const CHUNK_BYTES_PER_WORKER = 2 * 1024 * 1024;
|
|
68
|
+
/**
|
|
69
|
+
* Target jobs-per-worker per dispatch. More jobs than workers gives the pool's
|
|
70
|
+
* idle-slot assignment room to load-balance (a slow job doesn't strand a worker
|
|
71
|
+
* while the rest finish early). Drives the derived `subBatchMaxBytes`.
|
|
72
|
+
*/
|
|
73
|
+
const TARGET_JOBS_PER_WORKER = 3;
|
|
74
|
+
/** Floor for a derived sub-batch so jobs don't shrink to per-file IPC churn. */
|
|
75
|
+
const MIN_SUB_BATCH_BYTES = 256 * 1024;
|
|
76
|
+
function resolveChunkByteBudget(options, effectivePoolSize = 1) {
|
|
61
77
|
const opt = options?.chunkByteBudget;
|
|
62
78
|
if (typeof opt === 'number' && Number.isFinite(opt) && opt > 0)
|
|
63
79
|
return opt;
|
|
64
80
|
const env = Number(process.env.GITNEXUS_CHUNK_BYTE_BUDGET);
|
|
65
81
|
if (Number.isFinite(env) && env > 0)
|
|
66
82
|
return env;
|
|
67
|
-
|
|
83
|
+
// Auto: size each chunk so a dispatch can fan across the whole pool. A
|
|
84
|
+
// single-worker (tiny-repo) run keeps the original 2 MB invalidation floor.
|
|
85
|
+
return Math.max(DEFAULT_CHUNK_BYTE_BUDGET, effectivePoolSize * CHUNK_BYTES_PER_WORKER);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Handle a worker-pool startup failure by FAILING FAST with the captured cause
|
|
89
|
+
* (#1741). The pool self-heals *transient* worker crashes on its own — a
|
|
90
|
+
* bounded, jittered startup restart loop (see worker-pool.ts) — so this is
|
|
91
|
+
* reached only when that self-heal is EXHAUSTED, or a deterministic crash-loop
|
|
92
|
+
* was detected, or the pool could not even be constructed. In every such case
|
|
93
|
+
* the workers genuinely cannot start.
|
|
94
|
+
*
|
|
95
|
+
* There is no sequential parser to silently degrade to — that fallback was
|
|
96
|
+
* removed (and it had masked a worker-startup regression as a 2-hour "stuck"
|
|
97
|
+
* run in #1741, rc99: a dropped `logger.warn` plus an unbounded sequential
|
|
98
|
+
* grind). GitNexus surfaces the real crash and aborts so the operator fixes the
|
|
99
|
+
* worker startup (commonly a missing build). The pool's own crash
|
|
100
|
+
* classification (`crashClass` on WorkerPoolInitializationError) sharpens the
|
|
101
|
+
* message.
|
|
102
|
+
*
|
|
103
|
+
* @throws always — an actionable Error carrying the captured worker crash.
|
|
104
|
+
* @internal Exported for unit tests; production callers are the parse loop's
|
|
105
|
+
* two worker-startup catch sites below.
|
|
106
|
+
*/
|
|
107
|
+
export function handleWorkerStartupFailure(err) {
|
|
108
|
+
const isInit = err instanceof WorkerPoolInitializationError;
|
|
109
|
+
const readinessFailures = isInit ? err.readinessFailures : [];
|
|
110
|
+
const crashClass = isInit ? err.crashClass : undefined;
|
|
111
|
+
// Surface the real cause verbatim: readiness failures for an init crash, or
|
|
112
|
+
// the construction error message (e.g. "Worker script not found: …") when the
|
|
113
|
+
// pool never got to spawn workers.
|
|
114
|
+
const failureDetail = readinessFailures.length > 0
|
|
115
|
+
? ` Underlying worker failure(s): ${readinessFailures.join(' | ')}`
|
|
116
|
+
: isInit
|
|
117
|
+
? ''
|
|
118
|
+
: ` Underlying error: ${err.message}`;
|
|
119
|
+
// Always surface the real crash — never let a startup failure pass silently.
|
|
120
|
+
logger.error({ err: err.message, readinessFailures, crashClass }, 'Worker pool failed to start — workers could not start (bounded self-heal exhausted).');
|
|
121
|
+
const cause = crashClass === 'deterministic-startup'
|
|
122
|
+
? `every worker crashed identically during startup (a deterministic ` +
|
|
123
|
+
`crash-loop — retrying cannot help), so the pool has no usable workers.`
|
|
124
|
+
: isInit
|
|
125
|
+
? `workers exhausted the bounded startup retry budget without reporting ` +
|
|
126
|
+
`ready, so the pool has no usable workers.`
|
|
127
|
+
: `the worker pool could not be constructed.`;
|
|
128
|
+
// Class-aware fix hint: a missing/broken native binding is the likely cause
|
|
129
|
+
// when workers crashed during init, but it is the WRONG guess for a pool that
|
|
130
|
+
// never constructed (commonly a missing build / unresolvable worker path).
|
|
131
|
+
const fixHint = isInit
|
|
132
|
+
? `Fix the worker startup failure shown above (often a missing/broken native ` +
|
|
133
|
+
`binding or a top-of-script import error in parse-worker).`
|
|
134
|
+
: `Fix the worker pool construction error shown above (commonly a missing ` +
|
|
135
|
+
`build, so dist/ has no parse-worker, or an unresolvable worker path).`;
|
|
136
|
+
throw new Error(`Worker pool failed to start: ${cause}${failureDetail}\n\n` +
|
|
137
|
+
`The worker pool is GitNexus's only parse path — there is no sequential ` +
|
|
138
|
+
`fallback to hide this crash behind (silently degrading masked a ` +
|
|
139
|
+
`worker-startup regression as a 2-hour "stuck" run in #1741). Fix:\n` +
|
|
140
|
+
` • ${fixHint}`);
|
|
68
141
|
}
|
|
69
142
|
/**
|
|
70
143
|
* Chunked parse + resolve loop.
|
|
71
144
|
*
|
|
72
|
-
* Reads source in byte-budget chunks (~20MB each)
|
|
73
|
-
* 1. Parse via worker pool (
|
|
74
|
-
* 2.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
145
|
+
* Reads source in byte-budget chunks (~20MB each):
|
|
146
|
+
* 1. Parse each chunk via the worker pool (the sole parse path)
|
|
147
|
+
* 2. After all chunks parse, emit route CALLS edges (deferred so resolution
|
|
148
|
+
* sees the full repo graph) and collect the exported-type map
|
|
149
|
+
* 3. Collect TypeEnv bindings for cross-file propagation
|
|
150
|
+
*
|
|
151
|
+
* Import, call, and inheritance edges are emitted by the scope-resolution
|
|
152
|
+
* phase, not here (RING4-1 #942 / RING4-2 #943 removed the legacy passes).
|
|
79
153
|
*/
|
|
80
154
|
export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, totalFiles, repoPath, pipelineStart, onProgress, options) {
|
|
81
|
-
const
|
|
82
|
-
const symbolTable =
|
|
155
|
+
const model = createSemanticModel();
|
|
156
|
+
const symbolTable = model.symbols;
|
|
83
157
|
const parseableScanned = scannedFiles.filter((f) => {
|
|
84
158
|
const lang = getLanguageFromFilename(f.path);
|
|
85
159
|
return lang && isLanguageAvailable(lang);
|
|
@@ -104,6 +178,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
104
178
|
// Linux-built cache misses on macOS for the same repo.
|
|
105
179
|
parseableScanned.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
106
180
|
const totalParseable = parseableScanned.length;
|
|
181
|
+
const totalBytes = parseableScanned.reduce((sum, f) => sum + f.size, 0);
|
|
107
182
|
if (totalParseable === 0) {
|
|
108
183
|
onProgress({
|
|
109
184
|
phase: 'parsing',
|
|
@@ -115,6 +190,27 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
115
190
|
stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount },
|
|
116
191
|
});
|
|
117
192
|
}
|
|
193
|
+
// Sequential parsing has been removed: the worker pool (quarantine +
|
|
194
|
+
// respawn/recycle + circuit breaker) is the sole parse path. The three
|
|
195
|
+
// channels that used to select an in-process parser are now hard errors, so
|
|
196
|
+
// an operator who set one gets an actionable message instead of a silently
|
|
197
|
+
// slower (now nonexistent) fallback. Validated before any chunk work; a
|
|
198
|
+
// zero-parseable-file repo is exempt (nothing to parse).
|
|
199
|
+
if (totalParseable > 0) {
|
|
200
|
+
const requestedPoolSize = options?.workerPoolSize;
|
|
201
|
+
const disabledByEnv = requestedPoolSize === undefined && workerPoolDisabledByEnv();
|
|
202
|
+
if (options?.skipWorkers || requestedPoolSize === 0 || disabledByEnv) {
|
|
203
|
+
const reason = options?.skipWorkers
|
|
204
|
+
? '`skipWorkers: true` was passed'
|
|
205
|
+
: requestedPoolSize === 0
|
|
206
|
+
? '`--workers 0` (workerPoolSize=0) was requested'
|
|
207
|
+
: '`GITNEXUS_WORKER_POOL_SIZE=0` is set';
|
|
208
|
+
throw new WorkerPoolDisabledError(`Worker-pool parsing cannot be disabled (${reason}). GitNexus no longer ` +
|
|
209
|
+
`has a sequential parser — the worker pool self-heals via quarantine + ` +
|
|
210
|
+
`respawn, so there is no slower path to fall back to. Pass ` +
|
|
211
|
+
`\`--workers <N>\` with N>=1, or omit it for an auto-sized pool.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
118
214
|
// Build byte-budget chunks. The budget is resolved per-call (U14): options
|
|
119
215
|
// first, then env, then the built-in default. Pre-U14 this was a
|
|
120
216
|
// module-load IIFE constant, which froze the env value at import time
|
|
@@ -122,7 +218,28 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
122
218
|
// runs. Resolving in the function body restores per-call configurability
|
|
123
219
|
// and matches the pattern used by resolveAutoPoolSize and the U1
|
|
124
220
|
// parseChunkConcurrency resolver.
|
|
125
|
-
|
|
221
|
+
// Effective worker count, computed up-front so the chunk budget can scale to
|
|
222
|
+
// keep the whole pool busy (#worker-idle). The pool is ALWAYS used (sequential
|
|
223
|
+
// parsing was removed; the disabled channels threw above). Size it to the
|
|
224
|
+
// work: an explicit `--workers <N>` pins the size; otherwise the cores-based
|
|
225
|
+
// auto size is capped by the repo's worth of work (~one worker per
|
|
226
|
+
// CHUNK_BYTES_PER_WORKER of source) so a tiny repo spawns ~1 worker instead of
|
|
227
|
+
// a full pool, replacing the job the deleted small-repo threshold used to do.
|
|
228
|
+
// KTD-3 of the remove-sequential plan; the cap formula is intentionally coarse
|
|
229
|
+
// (tuning deferred).
|
|
230
|
+
const explicitPoolSize = options?.workerPoolSize;
|
|
231
|
+
const workProportionalCap = Math.max(1, Math.ceil(totalBytes / CHUNK_BYTES_PER_WORKER));
|
|
232
|
+
const effectivePoolSize = explicitPoolSize && explicitPoolSize > 0
|
|
233
|
+
? explicitPoolSize
|
|
234
|
+
: Math.min(resolveAutoPoolSize(), workProportionalCap);
|
|
235
|
+
const chunkByteBudget = resolveChunkByteBudget(options, effectivePoolSize);
|
|
236
|
+
// Sub-batch size so each chunk fans into ~`TARGET_JOBS_PER_WORKER` jobs per
|
|
237
|
+
// worker, giving the pool's idle-slot assignment room to load-balance. An
|
|
238
|
+
// explicit `GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES` operator override wins.
|
|
239
|
+
const subBatchEnv = Number(process.env.GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES);
|
|
240
|
+
const dispatchSubBatchMaxBytes = Number.isFinite(subBatchEnv) && subBatchEnv > 0
|
|
241
|
+
? subBatchEnv
|
|
242
|
+
: Math.max(MIN_SUB_BATCH_BYTES, Math.ceil(chunkByteBudget / (effectivePoolSize * TARGET_JOBS_PER_WORKER)));
|
|
126
243
|
const chunks = [];
|
|
127
244
|
let currentChunk = [];
|
|
128
245
|
let currentBytes = 0;
|
|
@@ -154,37 +271,25 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
154
271
|
stats: { filesProcessed: 0, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
155
272
|
});
|
|
156
273
|
}
|
|
157
|
-
//
|
|
158
|
-
// Test suites may lower the thresholds via `options.workerThresholdsForTest`
|
|
159
|
-
// to exercise the worker-pool path with small fixtures; see PipelineOptions.
|
|
160
|
-
const MIN_FILES_FOR_WORKERS = options?.workerThresholdsForTest?.minFiles ?? 15;
|
|
161
|
-
const MIN_BYTES_FOR_WORKERS = options?.workerThresholdsForTest?.minBytes ?? 512 * 1024;
|
|
162
|
-
const totalBytes = parseableScanned.reduce((s, f) => s + f.size, 0);
|
|
163
|
-
// Create worker pool lazily, reuse across cache-miss chunks.
|
|
274
|
+
// Create the worker pool lazily, reusing it across cache-miss chunks.
|
|
164
275
|
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
(totalParseable >= MIN_FILES_FOR_WORKERS || totalBytes >= MIN_BYTES_FOR_WORKERS);
|
|
276
|
+
// KTD-8 — the pool is intentionally NOT created before the parse-cache
|
|
277
|
+
// lookup: a warm-cache all-hit run must replay cached worker output without
|
|
278
|
+
// loading parse-worker.js or any tree-sitter/N-API native bindings. So
|
|
279
|
+
// `getOrCreateWorkerPool` is called only from inside the chunk loop, on the
|
|
280
|
+
// first cache MISS. There is no longer a "should we use workers?" gate:
|
|
281
|
+
// sequential parsing was removed and the disabled channels (`--workers 0` /
|
|
282
|
+
// env=0 / `skipWorkers`) threw above, so for any repo with parseable files
|
|
283
|
+
// the pool is always the parse path.
|
|
174
284
|
let workerPool;
|
|
175
|
-
let workerPoolDisabled = false;
|
|
176
285
|
const getOrCreateWorkerPool = () => {
|
|
177
|
-
if (!shouldUseWorkers || workerPoolDisabled)
|
|
178
|
-
return undefined;
|
|
179
286
|
if (workerPool)
|
|
180
287
|
return workerPool;
|
|
181
288
|
try {
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// mocking the module import graph. When unset, the normal src/
|
|
187
|
-
// → dist/ resolution runs.
|
|
289
|
+
// Test-only injection: integration tests pass a custom worker script URL
|
|
290
|
+
// via `workerUrlForTest` so they can drive the chunk-loop with
|
|
291
|
+
// deterministically-misbehaving workers without mocking the module import
|
|
292
|
+
// graph. When unset, the normal src/ → dist/ resolution runs.
|
|
188
293
|
let workerUrl = options?.workerUrlForTest ?? new URL('../workers/parse-worker.js', import.meta.url);
|
|
189
294
|
// When running under vitest, import.meta.url points to src/ where no .js exists.
|
|
190
295
|
// Fall back to the compiled dist/ worker so the pool can spawn real worker threads.
|
|
@@ -195,44 +300,35 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
195
300
|
workerUrl = pathToFileURL(distWorker);
|
|
196
301
|
}
|
|
197
302
|
}
|
|
198
|
-
|
|
303
|
+
// Thread the ParsedFile store path into the pool so workers write their
|
|
304
|
+
// own shards (#1983 parallel serialization). `parsedFileStorePath` is
|
|
305
|
+
// declared below but this closure only runs from inside the chunk loop,
|
|
306
|
+
// after it is initialized; `undefined` drives the worker no-store
|
|
307
|
+
// fallback (return ParsedFiles in the result).
|
|
308
|
+
workerPool = createWorkerPool(workerUrl, effectivePoolSize, {
|
|
309
|
+
parsedFileStoreStoragePath: parsedFileStorePath,
|
|
310
|
+
// Durable, content-addressed shard dir for warm-cache reuse (#2038).
|
|
311
|
+
// Initialized below before the chunk loop (same deferred-init pattern
|
|
312
|
+
// as `parsedFileStorePath`); this closure only runs from the loop.
|
|
313
|
+
durableParsedFileStoragePath: durableParsedFileDir,
|
|
314
|
+
// Fan each chunk across the whole pool (#worker-idle): without this a
|
|
315
|
+
// chunk smaller than the 8 MB sub-batch cap became a single job on a
|
|
316
|
+
// single worker. Honors an explicit `subBatchMaxBytes` / env override.
|
|
317
|
+
subBatchMaxBytes: dispatchSubBatchMaxBytes,
|
|
318
|
+
});
|
|
199
319
|
return workerPool;
|
|
200
320
|
}
|
|
201
321
|
catch (err) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
322
|
+
// Pool *construction* failed (e.g. the worker script is missing — a
|
|
323
|
+
// broken install). Fail fast with the cause (#1741). There is no
|
|
324
|
+
// sequential parser to fall back to; the operator must fix the worker
|
|
325
|
+
// startup (commonly a missing build so dist/ has no parse-worker).
|
|
326
|
+
handleWorkerStartupFailure(err);
|
|
205
327
|
}
|
|
206
328
|
};
|
|
207
329
|
let filesParsedSoFar = 0;
|
|
208
|
-
// Two caches with different lifetimes:
|
|
209
|
-
// - `astCache` (chunk-local, cleared between chunks) — call /
|
|
210
|
-
// heritage / import processors read it during parse to avoid
|
|
211
|
-
// re-parsing within the same chunk.
|
|
212
|
-
// - `scopeTreeCache` (total-parseable-sized, never cleared by
|
|
213
|
-
// parse-impl) — exposed via ParseOutput so scope-resolution can
|
|
214
|
-
// skip a second tree-sitter parse. Worker-mode parses don't
|
|
215
|
-
// populate either; consumers fall back to a fresh parse.
|
|
216
|
-
// See plan docs/plans/2026-04-20-002-perf-parse-heritage-mro-plan.md (Unit 4).
|
|
217
|
-
const maxChunkFiles = chunks.reduce((max, c) => Math.max(max, c.length), 0);
|
|
218
|
-
let astCache = createASTCache(maxChunkFiles);
|
|
219
|
-
const scopeTreeCache = createASTCache(Math.max(parseableScanned.length, 1));
|
|
220
|
-
// Build import resolution context once — suffix index, file lists, resolve cache.
|
|
221
|
-
const importCtx = buildImportResolutionContext(allPaths);
|
|
222
|
-
const allPathObjects = allPaths.map((p) => ({ path: p }));
|
|
223
|
-
const sequentialChunkPaths = [];
|
|
224
|
-
const chunkNeedsSynthesis = chunks.map((paths) => paths.some((p) => {
|
|
225
|
-
const lang = getLanguageFromFilename(p);
|
|
226
|
-
return lang != null && needsSynthesis(lang);
|
|
227
|
-
}));
|
|
228
330
|
const exportedTypeMap = new Map();
|
|
229
331
|
const bindingAccumulator = new BindingAccumulator();
|
|
230
|
-
// Tracks whether per-chunk or fallback wildcard-binding synthesis already
|
|
231
|
-
// ran, so the unconditional final call below can be skipped when redundant.
|
|
232
|
-
// synthesizeWildcardImportBindings is graph-global; once any chunk runs it
|
|
233
|
-
// after parsing wildcard files, later non-wildcard chunks add no work for
|
|
234
|
-
// it, and later wildcard chunks re-run it themselves.
|
|
235
|
-
let hasSynthesized = false;
|
|
236
332
|
const allFetchCalls = [];
|
|
237
333
|
const allFetchWrapperDefs = [];
|
|
238
334
|
const allExtractedRoutes = [];
|
|
@@ -242,20 +338,6 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
242
338
|
const allRouterModuleAliases = [];
|
|
243
339
|
const allToolDefs = [];
|
|
244
340
|
const allORMQueries = [];
|
|
245
|
-
const deferredWorkerCalls = [];
|
|
246
|
-
const deferredWorkerHeritage = [];
|
|
247
|
-
const deferredConstructorBindings = [];
|
|
248
|
-
const deferredAssignments = [];
|
|
249
|
-
// Imports accumulated across chunks. Previously processed per-chunk
|
|
250
|
-
// via `processImportsFromExtracted` inside the chunk loop, which
|
|
251
|
-
// forced workers to sit idle on the main thread's extraction pass
|
|
252
|
-
// between chunk dispatches (4-5% CPU utilization symptom). Deferring
|
|
253
|
-
// to a single end-of-loop pass lets the worker pool start chunk N+1
|
|
254
|
-
// immediately after chunk N's worker dispatch returns. Resolution is
|
|
255
|
-
// strictly-more-information at end-of-loop because graph now has
|
|
256
|
-
// every chunk's symbols — improves cross-chunk import targets.
|
|
257
|
-
const deferredWorkerImports = [];
|
|
258
|
-
let anyChunkNeedsWildcardSynth = false;
|
|
259
341
|
// Aggregated per-file ParsedFile artifacts produced by workers' calls
|
|
260
342
|
// to `extractParsedFile`. Threaded through to the scope-resolution
|
|
261
343
|
// phase so it can SKIP its own re-extraction on cache hits — this is
|
|
@@ -267,6 +349,29 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
267
349
|
// run's, replay the cached ParseWorkerResult[] instead of dispatching
|
|
268
350
|
// to workers. See gitnexus/src/storage/parse-cache.ts.
|
|
269
351
|
const parseCache = options?.parseCache;
|
|
352
|
+
// Disk-backed ParsedFile store (#1983): when a storage path is available we
|
|
353
|
+
// flush worker-produced ParsedFiles to disk per chunk (instead of retaining
|
|
354
|
+
// them in `allParsedFiles`) and scope-resolution streams them back per
|
|
355
|
+
// language — avoiding both the ~1× semantic-model RAM cost of holding them
|
|
356
|
+
// and, critically, the unbounded native tree-sitter re-parse leak that
|
|
357
|
+
// scope-resolution's main-thread re-extraction otherwise accumulates. When
|
|
358
|
+
// there is no storage path (tests / direct pipeline calls), we fall back to
|
|
359
|
+
// retaining them in `allParsedFiles` (small-repo path, preserves prior
|
|
360
|
+
// behavior). Cleared up-front so a prior run's shards never leak in.
|
|
361
|
+
const parsedFileStorePath = parseCache?.storagePath;
|
|
362
|
+
if (parsedFileStorePath)
|
|
363
|
+
await clearParsedFileStore(parsedFileStorePath);
|
|
364
|
+
// Durable, content-addressed ParsedFile store (#2038 warm-cache coverage) —
|
|
365
|
+
// a sibling of the run-scoped store, NOT cleared per run. Workers write a
|
|
366
|
+
// shard per chunk hash; on a warm parse-cache hit we restore the chunk's
|
|
367
|
+
// shards into the run-scoped store so scope-resolution streams them without
|
|
368
|
+
// re-parsing. `durableHitKeys` is the prior run's index, version-gated by
|
|
369
|
+
// PARSE_CACHE_VERSION (a mismatch ⇒ empty ⇒ every chunk re-dispatches, which
|
|
370
|
+
// repopulates the durable store — never the main-thread extract fallback).
|
|
371
|
+
const durableParsedFileDir = parsedFileStorePath !== undefined ? getDurableParsedFileDir(parsedFileStorePath) : undefined;
|
|
372
|
+
const durableHitKeys = durableParsedFileDir !== undefined
|
|
373
|
+
? await loadDurableParsedFileIndex(durableParsedFileDir, PARSE_CACHE_VERSION)
|
|
374
|
+
: new Set();
|
|
270
375
|
let chunkCacheHits = 0;
|
|
271
376
|
let chunkCacheMisses = 0;
|
|
272
377
|
try {
|
|
@@ -305,7 +410,125 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
305
410
|
// body, which re-read process.env on every iteration even though
|
|
306
411
|
// the env can't change mid-run.
|
|
307
412
|
const verboseThroughputLog = isDev || isVerboseIngestionEnabled();
|
|
413
|
+
const heapProbeEveryN = isDebugHeapEnabled() ? 25 : 0;
|
|
414
|
+
let pendingWorkerChunk = null;
|
|
415
|
+
// Apply one chunk's merged worker data: per-chunk aggregation into the
|
|
416
|
+
// run-level accumulators + the throughput log. Shared by the cache-hit
|
|
417
|
+
// (inline) and worker (deferred) paths. The `| null` guard is defensive —
|
|
418
|
+
// every live caller passes real worker data now that sequential parsing
|
|
419
|
+
// (which was the only path that passed null) is gone.
|
|
420
|
+
const applyChunkResults = async (chunkWorkerData, chunkIdx, chunkFiles, chunkStartMs) => {
|
|
421
|
+
if (chunkWorkerData) {
|
|
422
|
+
if (chunkWorkerData.parsedFiles?.length) {
|
|
423
|
+
if (parsedFileStorePath) {
|
|
424
|
+
await persistParsedFileChunk(parsedFileStorePath, `chunk-${chunkIdx}`, chunkWorkerData.parsedFiles);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
for (const item of chunkWorkerData.parsedFiles)
|
|
428
|
+
allParsedFiles.push(item);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (chunkWorkerData.fileScopeBindings?.length) {
|
|
432
|
+
for (const { filePath, bindings } of chunkWorkerData.fileScopeBindings) {
|
|
433
|
+
if (typeof filePath !== 'string' || filePath.length === 0)
|
|
434
|
+
continue;
|
|
435
|
+
if (!Array.isArray(bindings))
|
|
436
|
+
continue;
|
|
437
|
+
const entries = [];
|
|
438
|
+
for (const tuple of bindings) {
|
|
439
|
+
if (!Array.isArray(tuple) || tuple.length !== 2)
|
|
440
|
+
continue;
|
|
441
|
+
const [varName, typeName] = tuple;
|
|
442
|
+
if (typeof varName !== 'string' || typeof typeName !== 'string')
|
|
443
|
+
continue;
|
|
444
|
+
entries.push({ scope: '', varName, typeName });
|
|
445
|
+
}
|
|
446
|
+
if (entries.length > 0) {
|
|
447
|
+
bindingAccumulator.appendFile(filePath, entries);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (chunkWorkerData.fetchCalls?.length) {
|
|
452
|
+
for (const item of chunkWorkerData.fetchCalls)
|
|
453
|
+
allFetchCalls.push(item);
|
|
454
|
+
}
|
|
455
|
+
if (chunkWorkerData.fetchWrapperDefs?.length) {
|
|
456
|
+
for (const item of chunkWorkerData.fetchWrapperDefs)
|
|
457
|
+
allFetchWrapperDefs.push(item);
|
|
458
|
+
}
|
|
459
|
+
if (chunkWorkerData.routes?.length) {
|
|
460
|
+
for (const item of chunkWorkerData.routes)
|
|
461
|
+
allExtractedRoutes.push(item);
|
|
462
|
+
}
|
|
463
|
+
if (chunkWorkerData.decoratorRoutes?.length) {
|
|
464
|
+
for (const item of chunkWorkerData.decoratorRoutes)
|
|
465
|
+
allDecoratorRoutes.push(item);
|
|
466
|
+
}
|
|
467
|
+
if (chunkWorkerData.routerIncludes?.length) {
|
|
468
|
+
for (const item of chunkWorkerData.routerIncludes)
|
|
469
|
+
allRouterIncludes.push(item);
|
|
470
|
+
}
|
|
471
|
+
if (chunkWorkerData.routerImports?.length) {
|
|
472
|
+
for (const item of chunkWorkerData.routerImports)
|
|
473
|
+
allRouterImports.push(item);
|
|
474
|
+
}
|
|
475
|
+
if (chunkWorkerData.routerModuleAliases?.length) {
|
|
476
|
+
for (const item of chunkWorkerData.routerModuleAliases)
|
|
477
|
+
allRouterModuleAliases.push(item);
|
|
478
|
+
}
|
|
479
|
+
if (chunkWorkerData.toolDefs?.length) {
|
|
480
|
+
for (const item of chunkWorkerData.toolDefs)
|
|
481
|
+
allToolDefs.push(item);
|
|
482
|
+
}
|
|
483
|
+
if (chunkWorkerData.ormQueries?.length) {
|
|
484
|
+
for (const item of chunkWorkerData.ormQueries)
|
|
485
|
+
allORMQueries.push(item);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
filesParsedSoFar += chunkFiles.length;
|
|
489
|
+
if (verboseThroughputLog && chunkStartMs !== null) {
|
|
490
|
+
const elapsedMs = Date.now() - chunkStartMs;
|
|
491
|
+
const filesPerSec = elapsedMs > 0 ? (chunkFiles.length * 1000) / elapsedMs : 0;
|
|
492
|
+
const stats = workerPool?.getStats?.();
|
|
493
|
+
const poolFrag = stats
|
|
494
|
+
? ` pool: ${stats.activeSlots}/${stats.size} active, ` +
|
|
495
|
+
`${stats.quarantined} quarantined${stats.poolBroken ? ', BROKEN' : ''}`
|
|
496
|
+
: ' (cache replay)';
|
|
497
|
+
logger.info(`📊 chunk ${chunkIdx + 1}/${numChunks}: ${chunkFiles.length} files in ${elapsedMs}ms ` +
|
|
498
|
+
`(${filesPerSec.toFixed(1)} files/s)${poolFrag}`);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
// Merge + finalize a parked worker chunk: graph merge (the overlapped
|
|
502
|
+
// main-thread step) → parse-cache write-guard → run-level aggregation.
|
|
503
|
+
const finalizeWorkerChunk = async (p) => {
|
|
504
|
+
const chunkWorkerData = mergeChunkResults(graph, symbolTable, p.rawResults, exportedTypeMap);
|
|
505
|
+
// Persist raw results for this chunk hash (skipping when any chunk file
|
|
506
|
+
// was worker-quarantined, so the narrower rawResults isn't cached under
|
|
507
|
+
// the full-chunk key — see the original inline note / U20.U2).
|
|
508
|
+
if (parseCache && p.chunkHash && p.rawResults.length > 0) {
|
|
509
|
+
const quarantineSet = new Set(workerPool?.getQuarantinedPaths?.() ?? []);
|
|
510
|
+
const chunkHadQuarantine = p.chunkFiles.some((f) => quarantineSet.has(f.path));
|
|
511
|
+
if (chunkHadQuarantine) {
|
|
512
|
+
if (isDev) {
|
|
513
|
+
const quarantinedInChunk = p.chunkFiles.filter((f) => quarantineSet.has(f.path)).length;
|
|
514
|
+
logger.info(`📦 parse-cache SKIP: chunk ${p.chunkIdx + 1}/${numChunks} ` +
|
|
515
|
+
`had ${quarantinedInChunk} worker-quarantined file(s); ` +
|
|
516
|
+
`next run will rediscover (${p.chunkHash.slice(0, 8)})`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
await persistParseCacheChunk(parseCache, p.chunkHash, p.rawResults);
|
|
521
|
+
if (isDev) {
|
|
522
|
+
logger.info(`📦 parse-cache MISS+store: chunk ${p.chunkIdx + 1}/${numChunks} (${p.chunkFiles.length} files, ${p.chunkHash.slice(0, 8)})`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
await applyChunkResults(chunkWorkerData, p.chunkIdx, p.chunkFiles, p.chunkStartMs);
|
|
527
|
+
};
|
|
308
528
|
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
529
|
+
if (heapProbeEveryN > 0 && chunkIdx > 0 && chunkIdx % heapProbeEveryN === 0) {
|
|
530
|
+
logHeapProbe(`parse-chunk-${chunkIdx}`, `nodes=${graph.nodeCount} parsedFiles=${allParsedFiles.length}`);
|
|
531
|
+
}
|
|
309
532
|
const chunkPaths = chunks[chunkIdx];
|
|
310
533
|
// Start wall-clock for the per-chunk throughput log emitted at end
|
|
311
534
|
// of this iteration. The gate is computed once above; here we just
|
|
@@ -337,18 +560,30 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
337
560
|
}));
|
|
338
561
|
chunkHash = computeChunkHash(entries);
|
|
339
562
|
}
|
|
340
|
-
|
|
341
|
-
const cachedRaw = chunkHash && parseCache ? parseCache.entries.get(chunkHash) : undefined;
|
|
563
|
+
const cachedRaw = chunkHash && parseCache ? await loadParseCacheChunk(parseCache, chunkHash) : undefined;
|
|
342
564
|
// Track every chunk hash we touched so the orchestrator can
|
|
343
565
|
// prune stale entries (chunks whose composition no longer
|
|
344
566
|
// corresponds to a live chunk in the current scan) before saving.
|
|
345
567
|
if (parseCache && chunkHash)
|
|
346
568
|
parseCache.usedKeys.add(chunkHash);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
569
|
+
// A parse-cache hit may skip the workers ONLY if the chunk's ParsedFiles
|
|
570
|
+
// are recoverable without a main-thread re-parse: restored from a durable
|
|
571
|
+
// shard (store path) or carried in the cached result (no-store path). If a
|
|
572
|
+
// cached chunk's durable shards are missing — first run after the durable
|
|
573
|
+
// store was introduced, or a pruned/version-stale shard — fall through to
|
|
574
|
+
// a worker re-dispatch to repopulate them. NEVER let scope-resolution
|
|
575
|
+
// re-extract on the main thread (the #1983 OOM the durable store closes).
|
|
576
|
+
const durableHit = chunkHash !== null && durableParsedFileDir !== undefined && durableHitKeys.has(chunkHash);
|
|
577
|
+
if (cachedRaw && cachedRaw.length > 0 && (durableHit || parsedFileStorePath === undefined)) {
|
|
578
|
+
// Cache hit: replay cached worker output. Finalize any parked worker
|
|
579
|
+
// chunk FIRST so deferred aggregation stays in chunk order, then merge
|
|
580
|
+
// + apply this hit inline (no worker dispatch to overlap).
|
|
581
|
+
if (pendingWorkerChunk) {
|
|
582
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
583
|
+
pendingWorkerChunk = null;
|
|
584
|
+
}
|
|
350
585
|
chunkCacheHits++;
|
|
351
|
-
chunkWorkerData = mergeChunkResults(graph, symbolTable, cachedRaw);
|
|
586
|
+
const chunkWorkerData = mergeChunkResults(graph, symbolTable, cachedRaw, exportedTypeMap);
|
|
352
587
|
if (isDev) {
|
|
353
588
|
logger.info(`📦 parse-cache HIT: chunk ${chunkIdx + 1}/${numChunks} (${chunkFiles.length} files, ${chunkHash?.slice(0, 8) ?? 'unknown'})`);
|
|
354
589
|
}
|
|
@@ -368,12 +603,22 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
368
603
|
nodesCreated: graph.nodeCount,
|
|
369
604
|
},
|
|
370
605
|
});
|
|
606
|
+
// Restore the chunk's durable ParsedFile shards into the run-scoped
|
|
607
|
+
// store so scope-resolution finds full coverage with ZERO main-thread
|
|
608
|
+
// re-parse. A verbatim byte copy — byte-identical to a cold run.
|
|
609
|
+
if (durableHit && durableParsedFileDir && parsedFileStorePath && chunkHash) {
|
|
610
|
+
const restored = await restoreDurableParsedFileShard(durableParsedFileDir, parsedFileStorePath, chunkHash);
|
|
611
|
+
if (restored === 0) {
|
|
612
|
+
logger.warn(`parsedfile-cache: durable shards missing for cached chunk ` +
|
|
613
|
+
`${chunkHash.slice(0, 8)} — scope-resolution will re-extract these files`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
await applyChunkResults(chunkWorkerData, chunkIdx, chunkFiles, chunkStartMs);
|
|
371
617
|
}
|
|
372
618
|
else {
|
|
373
619
|
// Cache miss: dispatch to workers, capture the raw results, store
|
|
374
620
|
// them under the chunk hash for the next run.
|
|
375
621
|
chunkCacheMisses++;
|
|
376
|
-
const rawResults = [];
|
|
377
622
|
const progressForChunk = (current, _total, filePath) => {
|
|
378
623
|
const globalCurrent = filesParsedSoFar + current;
|
|
379
624
|
// Parse phase covers 20-70 (M2). Deferred extraction handles 70-95.
|
|
@@ -391,315 +636,82 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
391
636
|
});
|
|
392
637
|
};
|
|
393
638
|
const activeWorkerPool = getOrCreateWorkerPool();
|
|
639
|
+
// Worker path — PIPELINE: kick off this chunk's dispatch, merge the
|
|
640
|
+
// PREVIOUS chunk while these workers parse, then park this chunk for
|
|
641
|
+
// the next iteration to merge (overlapping its parse). The deferred
|
|
642
|
+
// merge + parse-cache write-guard + aggregation all run in
|
|
643
|
+
// `finalizeWorkerChunk`, in chunk order. The pool is the sole parse
|
|
644
|
+
// path — `getOrCreateWorkerPool` returns a pool or throws.
|
|
645
|
+
const dispatchPromise = dispatchChunkParse(chunkFiles, activeWorkerPool, progressForChunk, undefined, chunkHash ?? undefined);
|
|
646
|
+
// Mark handled so a rejection during the overlap drain below isn't
|
|
647
|
+
// flagged as unhandled; the `await` re-throws it for real handling.
|
|
648
|
+
dispatchPromise.catch(() => { });
|
|
649
|
+
if (pendingWorkerChunk) {
|
|
650
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
651
|
+
pendingWorkerChunk = null;
|
|
652
|
+
}
|
|
653
|
+
let chunkResults;
|
|
394
654
|
try {
|
|
395
|
-
|
|
396
|
-
// Capture raw results only when we have a cache to write to —
|
|
397
|
-
// otherwise we'd retain extra arrays for nothing.
|
|
398
|
-
parseCache && chunkHash && activeWorkerPool ? rawResults : undefined);
|
|
655
|
+
chunkResults = await dispatchPromise;
|
|
399
656
|
}
|
|
400
657
|
catch (err) {
|
|
401
658
|
if (!(err instanceof WorkerPoolInitializationError))
|
|
402
659
|
throw err;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
workerPoolDisabled = true;
|
|
409
|
-
const failedPool = workerPool;
|
|
410
|
-
workerPool = undefined;
|
|
411
|
-
await failedPool?.terminate().catch(() => undefined);
|
|
412
|
-
chunkWorkerData = await processParsing(graph, chunkFiles, symbolTable, astCache, scopeTreeCache, progressForChunk, undefined, undefined);
|
|
413
|
-
}
|
|
414
|
-
// Persist the raw results for this chunk hash. Sequential path
|
|
415
|
-
// doesn't populate rawResults (it writes directly to graph), so
|
|
416
|
-
// small repos without worker pool simply don't cache. That's fine.
|
|
417
|
-
//
|
|
418
|
-
// U20.U2: refuse the write when any chunk file is in the
|
|
419
|
-
// worker pool's cumulative quarantine snapshot. The chunkHash
|
|
420
|
-
// is computed from EVERY file in the chunk, but the pool's
|
|
421
|
-
// Layer 3 quarantine filters quarantined files out of dispatch
|
|
422
|
-
// — so `rawResults` is narrower than the chunkHash key implies.
|
|
423
|
-
// Caching it would silently replay incomplete results on the
|
|
424
|
-
// next run with unchanged content (the corruption class Codex's
|
|
425
|
-
// adversarial review of PR #1693 flagged).
|
|
426
|
-
//
|
|
427
|
-
// Skipping the write means the next analyze gets a cache miss
|
|
428
|
-
// for this chunk and re-dispatches against a fresh worker pool
|
|
429
|
-
// (quarantine is session-scoped — `createQuarantine` is called
|
|
430
|
-
// per-pool at worker-pool.ts), giving the quarantined file
|
|
431
|
-
// another chance. If quarantine fires again, U20.U1's
|
|
432
|
-
// sequential gap-fill still produces a complete graph for this
|
|
433
|
-
// run; the cache just stays empty for this chunk until a fully-
|
|
434
|
-
// clean dispatch lands.
|
|
435
|
-
if (parseCache && chunkHash && rawResults.length > 0) {
|
|
436
|
-
const quarantineSnapshot = workerPool?.getQuarantinedPaths?.() ?? [];
|
|
437
|
-
const quarantineSet = new Set(quarantineSnapshot);
|
|
438
|
-
const chunkHadQuarantine = chunkFiles.some((f) => quarantineSet.has(f.path));
|
|
439
|
-
if (chunkHadQuarantine) {
|
|
440
|
-
if (isDev) {
|
|
441
|
-
const quarantinedInChunk = chunkFiles.filter((f) => quarantineSet.has(f.path)).length;
|
|
442
|
-
logger.info(`📦 parse-cache SKIP: chunk ${chunkIdx + 1}/${numChunks} ` +
|
|
443
|
-
`had ${quarantinedInChunk} worker-quarantined file(s); ` +
|
|
444
|
-
`next run will rediscover (${chunkHash.slice(0, 8)})`);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
parseCache.entries.set(chunkHash, rawResults);
|
|
449
|
-
if (isDev) {
|
|
450
|
-
logger.info(`📦 parse-cache MISS+store: chunk ${chunkIdx + 1}/${numChunks} (${chunkFiles.length} files, ${chunkHash.slice(0, 8)})`);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
660
|
+
// Every worker crashed during startup and the pool's bounded
|
|
661
|
+
// self-heal was exhausted. Fail fast (#1741) — there is no sequential
|
|
662
|
+
// parser to degrade to. `handleWorkerStartupFailure` always throws, so
|
|
663
|
+
// `chunkResults` stays definitely assigned for the parked chunk below.
|
|
664
|
+
handleWorkerStartupFailure(err);
|
|
453
665
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
// main-thread work between worker dispatches — workers sat idle
|
|
461
|
-
// and total CPU utilization plateaued at 4-5% on multi-core boxes.
|
|
462
|
-
// Deferring keeps workers busy chunk-after-chunk; resolution sees
|
|
463
|
-
// strictly-more-information (full repo graph) so cross-chunk import
|
|
464
|
-
// and heritage targets resolve at least as well as before.
|
|
465
|
-
if (chunkWorkerData) {
|
|
466
|
-
if (chunkNeedsSynthesis[chunkIdx]) {
|
|
467
|
-
anyChunkNeedsWildcardSynth = true;
|
|
468
|
-
}
|
|
469
|
-
const skipFile = new Set();
|
|
470
|
-
const checkFile = new Set();
|
|
471
|
-
const shouldAccumulate = (filePath) => {
|
|
472
|
-
if (checkFile.has(filePath))
|
|
473
|
-
return true;
|
|
474
|
-
if (skipFile.has(filePath))
|
|
475
|
-
return false;
|
|
476
|
-
const lang = getLanguageFromFilename(filePath);
|
|
477
|
-
if (lang !== null && isRegistryPrimary(lang)) {
|
|
478
|
-
skipFile.add(filePath);
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
checkFile.add(filePath);
|
|
482
|
-
return true;
|
|
666
|
+
pendingWorkerChunk = {
|
|
667
|
+
rawResults: chunkResults,
|
|
668
|
+
chunkIdx,
|
|
669
|
+
chunkHash,
|
|
670
|
+
chunkFiles,
|
|
671
|
+
chunkStartMs,
|
|
483
672
|
};
|
|
484
|
-
for (const item of chunkWorkerData.imports) {
|
|
485
|
-
if (shouldAccumulate(item.filePath))
|
|
486
|
-
deferredWorkerImports.push(item);
|
|
487
|
-
}
|
|
488
|
-
for (const item of chunkWorkerData.calls) {
|
|
489
|
-
if (shouldAccumulate(item.filePath))
|
|
490
|
-
deferredWorkerCalls.push(item);
|
|
491
|
-
}
|
|
492
|
-
for (const item of chunkWorkerData.heritage) {
|
|
493
|
-
if (shouldAccumulate(item.filePath))
|
|
494
|
-
deferredWorkerHeritage.push(item);
|
|
495
|
-
}
|
|
496
|
-
for (const item of chunkWorkerData.constructorBindings) {
|
|
497
|
-
if (shouldAccumulate(item.filePath))
|
|
498
|
-
deferredConstructorBindings.push(item);
|
|
499
|
-
}
|
|
500
|
-
// Aggregate worker-produced ParsedFile artifacts so scope-
|
|
501
|
-
// resolution can use them as a re-extraction cache (skips its
|
|
502
|
-
// own tree-sitter re-parse on warm runs).
|
|
503
|
-
if (chunkWorkerData.parsedFiles?.length) {
|
|
504
|
-
for (const item of chunkWorkerData.parsedFiles)
|
|
505
|
-
allParsedFiles.push(item);
|
|
506
|
-
}
|
|
507
|
-
if (chunkWorkerData.assignments?.length) {
|
|
508
|
-
for (const item of chunkWorkerData.assignments) {
|
|
509
|
-
if (shouldAccumulate(item.filePath))
|
|
510
|
-
deferredAssignments.push(item);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (chunkWorkerData.fileScopeBindings?.length) {
|
|
514
|
-
for (const { filePath, bindings } of chunkWorkerData.fileScopeBindings) {
|
|
515
|
-
if (typeof filePath !== 'string' || filePath.length === 0)
|
|
516
|
-
continue;
|
|
517
|
-
if (!Array.isArray(bindings))
|
|
518
|
-
continue;
|
|
519
|
-
const entries = [];
|
|
520
|
-
for (const tuple of bindings) {
|
|
521
|
-
if (!Array.isArray(tuple) || tuple.length !== 2)
|
|
522
|
-
continue;
|
|
523
|
-
const [varName, typeName] = tuple;
|
|
524
|
-
if (typeof varName !== 'string' || typeof typeName !== 'string')
|
|
525
|
-
continue;
|
|
526
|
-
entries.push({ scope: '', varName, typeName });
|
|
527
|
-
}
|
|
528
|
-
if (entries.length > 0) {
|
|
529
|
-
bindingAccumulator.appendFile(filePath, entries);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
if (chunkWorkerData.fetchCalls?.length) {
|
|
534
|
-
for (const item of chunkWorkerData.fetchCalls)
|
|
535
|
-
allFetchCalls.push(item);
|
|
536
|
-
}
|
|
537
|
-
if (chunkWorkerData.fetchWrapperDefs?.length) {
|
|
538
|
-
for (const item of chunkWorkerData.fetchWrapperDefs)
|
|
539
|
-
allFetchWrapperDefs.push(item);
|
|
540
|
-
}
|
|
541
|
-
if (chunkWorkerData.routes?.length) {
|
|
542
|
-
for (const item of chunkWorkerData.routes)
|
|
543
|
-
allExtractedRoutes.push(item);
|
|
544
|
-
}
|
|
545
|
-
if (chunkWorkerData.decoratorRoutes?.length) {
|
|
546
|
-
for (const item of chunkWorkerData.decoratorRoutes)
|
|
547
|
-
allDecoratorRoutes.push(item);
|
|
548
|
-
}
|
|
549
|
-
if (chunkWorkerData.routerIncludes?.length) {
|
|
550
|
-
for (const item of chunkWorkerData.routerIncludes)
|
|
551
|
-
allRouterIncludes.push(item);
|
|
552
|
-
}
|
|
553
|
-
if (chunkWorkerData.routerImports?.length) {
|
|
554
|
-
for (const item of chunkWorkerData.routerImports)
|
|
555
|
-
allRouterImports.push(item);
|
|
556
|
-
}
|
|
557
|
-
if (chunkWorkerData.routerModuleAliases?.length) {
|
|
558
|
-
for (const item of chunkWorkerData.routerModuleAliases)
|
|
559
|
-
allRouterModuleAliases.push(item);
|
|
560
|
-
}
|
|
561
|
-
if (chunkWorkerData.toolDefs?.length) {
|
|
562
|
-
for (const item of chunkWorkerData.toolDefs)
|
|
563
|
-
allToolDefs.push(item);
|
|
564
|
-
}
|
|
565
|
-
if (chunkWorkerData.ormQueries?.length) {
|
|
566
|
-
for (const item of chunkWorkerData.ormQueries)
|
|
567
|
-
allORMQueries.push(item);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
await processImports(graph, chunkFiles, astCache, ctx, undefined, repoPath, allPaths);
|
|
572
|
-
sequentialChunkPaths.push(chunkPaths);
|
|
573
|
-
}
|
|
574
|
-
filesParsedSoFar += chunkFiles.length;
|
|
575
|
-
astCache.clear();
|
|
576
|
-
// Throughput observability (U3): emit a per-chunk metrics line
|
|
577
|
-
// under verbose ingestion mode so operators can verify CPU
|
|
578
|
-
// utilization moved + tune `--workers` / batch sizes without
|
|
579
|
-
// guessing. Cheap snapshot — just reads pool closure state.
|
|
580
|
-
if (verboseThroughputLog && chunkStartMs !== null) {
|
|
581
|
-
const elapsedMs = Date.now() - chunkStartMs;
|
|
582
|
-
const filesPerSec = elapsedMs > 0 ? (chunkFiles.length * 1000) / elapsedMs : 0;
|
|
583
|
-
const stats = workerPool?.getStats?.();
|
|
584
|
-
const poolFrag = stats
|
|
585
|
-
? ` pool: ${stats.activeSlots}/${stats.size} active, ` +
|
|
586
|
-
`${stats.quarantined} quarantined${stats.poolBroken ? ', BROKEN' : ''}`
|
|
587
|
-
: ' (sequential)';
|
|
588
|
-
logger.info(`📊 chunk ${chunkIdx + 1}/${numChunks}: ${chunkFiles.length} files in ${elapsedMs}ms ` +
|
|
589
|
-
`(${filesPerSec.toFixed(1)} files/s)${poolFrag}`);
|
|
590
673
|
}
|
|
674
|
+
// (Per-chunk aggregation + parse-cache write + throughput log now run in
|
|
675
|
+
// `applyChunkResults` / `finalizeWorkerChunk` — see the merge-pipelining
|
|
676
|
+
// block above. Route/import/inheritance edges are emitted later: route
|
|
677
|
+
// resolution in the single end-of-loop pass below, the rest by the
|
|
678
|
+
// scope-resolution phase, RING4-2 #943.)
|
|
679
|
+
}
|
|
680
|
+
// Drain the final parked worker chunk — the last pipelined chunk has no
|
|
681
|
+
// successor to overlap its merge with, so merge + finalize it here.
|
|
682
|
+
if (pendingWorkerChunk) {
|
|
683
|
+
await finalizeWorkerChunk(pendingWorkerChunk);
|
|
684
|
+
pendingWorkerChunk = null;
|
|
591
685
|
}
|
|
592
686
|
if (isDev && parseCache && (chunkCacheHits > 0 || chunkCacheMisses > 0)) {
|
|
593
687
|
logger.info(`📦 parse-cache summary: ${chunkCacheHits} chunk hit(s), ${chunkCacheMisses} miss(es) across ${numChunks} chunk(s)`);
|
|
594
688
|
}
|
|
689
|
+
logHeapProbe('post-parse-chunks', `routes=${allExtractedRoutes.length} nodes=${graph.nodeCount} parsedFiles=${allParsedFiles.length}`);
|
|
595
690
|
// Deferred end-of-loop extraction (moved out of the per-chunk block):
|
|
596
|
-
// 1.
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
// sees the full repo graph instead of just current-and-earlier chunks.
|
|
604
|
-
// Deferred extraction band (M2 from PR #1693 review): the 4 stages below
|
|
605
|
-
// each get their own 5-10 point slice of the 70-95 range so percent
|
|
606
|
-
// advances monotonically through the (potentially long) resolution work
|
|
607
|
-
// instead of holding flat at 82. Stages that are skipped (zero-length
|
|
608
|
-
// input) leave their band as a no-op jump — the next stage still starts
|
|
609
|
-
// at its own band, preserving monotonicity.
|
|
610
|
-
// imports: 70 -> 75 (5)
|
|
611
|
-
// heritage: 75 -> 80 (5)
|
|
691
|
+
// 1. route resolution on all chunks' routes
|
|
692
|
+
// Resolution sees the full repo graph instead of just current-and-earlier
|
|
693
|
+
// chunks. Import, call, and inheritance edges are emitted by the
|
|
694
|
+
// scope-resolution phase, not here (RING4-1 #942 removed the legacy call
|
|
695
|
+
// DAG; RING4-2 #943 removed the legacy import-map resolution + wildcard
|
|
696
|
+
// synthesis). Progress band: the route stage gets a slice of the 70-95
|
|
697
|
+
// range; a zero-length input leaves its band as a no-op jump.
|
|
612
698
|
// routes: 80 -> 85 (5)
|
|
613
|
-
// calls: 85 -> 95 (10)
|
|
614
699
|
const deferredProfile = isDeferredResolutionProfileEnabled();
|
|
615
700
|
if (deferredProfile) {
|
|
616
|
-
logDeferredProfile(`deferred band start:
|
|
617
|
-
`calls=${deferredWorkerCalls.length} routes=${allExtractedRoutes.length}`);
|
|
701
|
+
logDeferredProfile(`deferred band start: routes=${allExtractedRoutes.length}`);
|
|
618
702
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
await processImportsFromExtracted(graph, allPathObjects, deferredWorkerImports, ctx, (current, total) => {
|
|
622
|
-
const ratio = total > 0 ? current / total : 1;
|
|
623
|
-
onProgress({
|
|
624
|
-
phase: 'parsing',
|
|
625
|
-
percent: 70 + Math.round(ratio * 5),
|
|
626
|
-
message: 'Resolving imports (all chunks)...',
|
|
627
|
-
detail: `${current}/${total} files`,
|
|
628
|
-
stats: {
|
|
629
|
-
filesProcessed: filesParsedSoFar,
|
|
630
|
-
totalFiles: totalParseable,
|
|
631
|
-
nodesCreated: graph.nodeCount,
|
|
632
|
-
},
|
|
633
|
-
});
|
|
634
|
-
}, repoPath, importCtx);
|
|
635
|
-
endTimer(tImports, (ms) => `processImportsFromExtracted: ${ms.toFixed(0)}ms (${deferredWorkerImports.length} import batches before drain)`);
|
|
636
|
-
// U15 (lightweight M1): processImportsFromExtracted is the sole
|
|
637
|
-
// consumer of `deferredWorkerImports`. Free the array now so the
|
|
638
|
-
// GC can reclaim the per-file ExtractedImport records before the
|
|
639
|
-
// heavier downstream stages run (heritage, routes, calls). Peak
|
|
640
|
-
// accumulator memory drops from O(repo) to O(repo - imports) for
|
|
641
|
-
// the remainder of the deferred phase. The future per-chunk
|
|
642
|
-
// streaming upgrade can rewrite this with the same correctness
|
|
643
|
-
// contract once profile data shows it's warranted.
|
|
644
|
-
deferredWorkerImports.length = 0;
|
|
645
|
-
}
|
|
646
|
-
if (anyChunkNeedsWildcardSynth) {
|
|
647
|
-
const tWildcard = startTimer(deferredProfile);
|
|
648
|
-
synthesizeWildcardImportBindings(graph, ctx);
|
|
649
|
-
hasSynthesized = true;
|
|
650
|
-
endTimer(tWildcard, (ms) => `synthesizeWildcardImportBindings: ${ms.toFixed(0)}ms`);
|
|
651
|
-
}
|
|
652
|
-
// L5 from PR #1693 review: populate `exportedTypeMap` from the in-progress
|
|
653
|
-
// graph BEFORE `seedCrossFileReceiverTypes` runs. Previously the seeding
|
|
654
|
-
// branch below was reached with `exportedTypeMap.size === 0` in the
|
|
655
|
-
// worker path (the map was only built at the post-parse block far below,
|
|
656
|
-
// AFTER the seeding branch), so the seed dead-coded itself silently and
|
|
657
|
-
// call resolution never got the cross-file receiver-type enrichment.
|
|
658
|
-
// The post-parse builder still runs as a defensive fallback on the
|
|
659
|
-
// sequential path; its `size === 0` guard means we don't pay the cost
|
|
660
|
-
// twice on the worker path.
|
|
703
|
+
// Populate `exportedTypeMap` from the in-progress graph so the post-parse
|
|
704
|
+
// enrichment pass (enrichExportedTypeMap) sees cross-file export types.
|
|
661
705
|
if (exportedTypeMap.size === 0 && graph.nodeCount > 0) {
|
|
662
|
-
|
|
706
|
+
logHeapProbe('pre-buildExportedTypeMapFromGraph');
|
|
707
|
+
const graphExports = buildExportedTypeMapFromGraph(graph, model.symbols);
|
|
663
708
|
for (const [fp, exports] of graphExports)
|
|
664
709
|
exportedTypeMap.set(fp, exports);
|
|
665
|
-
|
|
666
|
-
if (exportedTypeMap.size > 0 && ctx.namedImportMap.size > 0 && deferredWorkerCalls.length > 0) {
|
|
667
|
-
const { enrichedCount } = seedCrossFileReceiverTypes(deferredWorkerCalls, ctx.namedImportMap, exportedTypeMap);
|
|
668
|
-
if (enrichedCount > 0) {
|
|
669
|
-
// Two independent gates, not else-if: when both isDev AND
|
|
670
|
-
// deferredProfile are active, BOTH lines fire — log scrapers keyed
|
|
671
|
-
// on the original "🔗 E1" emoji marker keep matching, AND operators
|
|
672
|
-
// grepping the [deferred-profile] prefix see no gap between the
|
|
673
|
-
// wildcard-synth and heritage timings.
|
|
674
|
-
if (isDev) {
|
|
675
|
-
logger.info(`🔗 E1: Seeded ${enrichedCount} cross-file receiver types (all chunks)`);
|
|
676
|
-
}
|
|
677
|
-
if (deferredProfile) {
|
|
678
|
-
logDeferredProfile(`E1: seeded ${enrichedCount} cross-file receiver types (all chunks)`);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
if (deferredWorkerHeritage.length > 0) {
|
|
683
|
-
const tHeritage = startTimer(deferredProfile);
|
|
684
|
-
await processHeritageFromExtracted(graph, deferredWorkerHeritage, ctx, (current, total) => {
|
|
685
|
-
const ratio = total > 0 ? current / total : 1;
|
|
686
|
-
onProgress({
|
|
687
|
-
phase: 'parsing',
|
|
688
|
-
percent: 75 + Math.round(ratio * 5),
|
|
689
|
-
message: 'Resolving heritage (all chunks)...',
|
|
690
|
-
detail: `${current}/${total} records`,
|
|
691
|
-
stats: {
|
|
692
|
-
filesProcessed: filesParsedSoFar,
|
|
693
|
-
totalFiles: totalParseable,
|
|
694
|
-
nodesCreated: graph.nodeCount,
|
|
695
|
-
},
|
|
696
|
-
});
|
|
697
|
-
});
|
|
698
|
-
endTimer(tHeritage, (ms) => `processHeritageFromExtracted: ${ms.toFixed(0)}ms (${deferredWorkerHeritage.length} records)`);
|
|
710
|
+
logHeapProbe('post-buildExportedTypeMapFromGraph');
|
|
699
711
|
}
|
|
700
712
|
if (allExtractedRoutes.length > 0) {
|
|
701
713
|
const tRoutes = startTimer(deferredProfile);
|
|
702
|
-
await processRoutesFromExtracted(graph, allExtractedRoutes,
|
|
714
|
+
await processRoutesFromExtracted(graph, allExtractedRoutes, model, (current, total) => {
|
|
703
715
|
const ratio = total > 0 ? current / total : 1;
|
|
704
716
|
onProgress({
|
|
705
717
|
phase: 'parsing',
|
|
@@ -715,159 +727,27 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
715
727
|
});
|
|
716
728
|
endTimer(tRoutes, (ms) => `processRoutesFromExtracted: ${ms.toFixed(0)}ms (${allExtractedRoutes.length} routes)`);
|
|
717
729
|
}
|
|
718
|
-
let fullWorkerHeritageMap;
|
|
719
|
-
if (deferredWorkerHeritage.length > 0) {
|
|
720
|
-
const tBuildHeritage = startTimer(deferredProfile);
|
|
721
|
-
fullWorkerHeritageMap = buildHeritageMap(deferredWorkerHeritage, ctx, getHeritageStrategyForLanguage);
|
|
722
|
-
endTimer(tBuildHeritage, (ms) => `buildHeritageMap wall: ${ms.toFixed(0)}ms`);
|
|
723
|
-
}
|
|
724
|
-
else if (deferredProfile) {
|
|
725
|
-
logDeferredProfile('buildHeritageMap: skipped (no heritage records)');
|
|
726
|
-
}
|
|
727
|
-
// U15 (lightweight M1): buildHeritageMap is the LAST consumer of the
|
|
728
|
-
// raw `deferredWorkerHeritage` records — processCallsFromExtracted
|
|
729
|
-
// below reads from the derived `fullWorkerHeritageMap` instead. Free
|
|
730
|
-
// the raw heritage array now so the GC can reclaim it before the
|
|
731
|
-
// (potentially long) call-resolution stage. processHeritageFromExtracted
|
|
732
|
-
// earlier was a read-only consumer (pushed to graph, didn't drain).
|
|
733
|
-
deferredWorkerHeritage.length = 0;
|
|
734
|
-
if (deferredWorkerCalls.length > 0) {
|
|
735
|
-
if (deferredProfile) {
|
|
736
|
-
logDeferredProfile(`processCallsFromExtracted: starting (${deferredWorkerCalls.length} call sites, heritageMap=${fullWorkerHeritageMap !== undefined})`);
|
|
737
|
-
}
|
|
738
|
-
const tCalls = startTimer(deferredProfile);
|
|
739
|
-
await processCallsFromExtracted(graph, deferredWorkerCalls, ctx, (current, total) => {
|
|
740
|
-
const ratio = total > 0 ? current / total : 1;
|
|
741
|
-
onProgress({
|
|
742
|
-
phase: 'parsing',
|
|
743
|
-
// Calls is the longest deferred stage on real repos — give it the
|
|
744
|
-
// 10-point tail 85-95 so the progress bar visibly advances during
|
|
745
|
-
// call resolution instead of holding at 82 (M2).
|
|
746
|
-
percent: 85 + Math.round(ratio * 10),
|
|
747
|
-
message: 'Resolving calls (all chunks)...',
|
|
748
|
-
detail: `${current}/${total} files`,
|
|
749
|
-
stats: {
|
|
750
|
-
filesProcessed: filesParsedSoFar,
|
|
751
|
-
totalFiles: totalParseable,
|
|
752
|
-
nodesCreated: graph.nodeCount,
|
|
753
|
-
},
|
|
754
|
-
});
|
|
755
|
-
}, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined, fullWorkerHeritageMap, bindingAccumulator);
|
|
756
|
-
endTimer(tCalls, (ms) => `processCallsFromExtracted: ${ms.toFixed(0)}ms total`);
|
|
757
|
-
}
|
|
758
|
-
if (deferredAssignments.length > 0) {
|
|
759
|
-
processAssignmentsFromExtracted(graph, deferredAssignments, ctx, deferredConstructorBindings.length > 0 ? deferredConstructorBindings : undefined, bindingAccumulator);
|
|
760
|
-
}
|
|
761
|
-
// U15 (lightweight M1): all three arrays have had their last consumer
|
|
762
|
-
// by the time we reach this point — processCallsFromExtracted drained
|
|
763
|
-
// `deferredWorkerCalls` and read `deferredConstructorBindings`;
|
|
764
|
-
// processAssignmentsFromExtracted drained `deferredAssignments` and
|
|
765
|
-
// also read `deferredConstructorBindings`. Free them now so the
|
|
766
|
-
// function-scope references die before downstream graph-build /
|
|
767
|
-
// scope-resolution starts using its own working memory. Note: arrays
|
|
768
|
-
// returned in the function result object (allFetchCalls,
|
|
769
|
-
// allExtractedRoutes, allDecoratorRoutes, allToolDefs, allORMQueries,
|
|
770
|
-
// allParsedFiles) intentionally stay live — downstream consumers
|
|
771
|
-
// need them.
|
|
772
|
-
deferredWorkerCalls.length = 0;
|
|
773
|
-
deferredConstructorBindings.length = 0;
|
|
774
|
-
deferredAssignments.length = 0;
|
|
775
730
|
}
|
|
776
731
|
finally {
|
|
777
732
|
await workerPool?.terminate();
|
|
778
733
|
}
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
// 2. `bindingAccumulator.finalize()` runs before `crossFile` disposes the
|
|
787
|
-
// accumulator downstream — callers that inspect partial TypeEnv state
|
|
788
|
-
// (or consume it via `enrichExportedTypeMap` on a partial recovery)
|
|
789
|
-
// still see a finalized accumulator.
|
|
790
|
-
// 3. `enrichExportedTypeMap` runs so any bindings already accumulated
|
|
791
|
-
// are propagated into `exportedTypeMap` even if the fallback aborted.
|
|
792
|
-
//
|
|
793
|
-
// Disposal of the accumulator remains with `crossFile` (owned by U2). We do
|
|
794
|
-
// NOT call `bindingAccumulator.dispose()` here.
|
|
734
|
+
// Fetch calls + ORM queries were already extracted inside each worker
|
|
735
|
+
// (returned in ParseWorkerResult, aggregated per chunk in applyChunkResults).
|
|
736
|
+
// With sequential parsing removed there is no post-loop drain to run — only
|
|
737
|
+
// the TypeEnv finalize + enrichment that the drain's `finally` used to host.
|
|
738
|
+
// Finalize the accumulator and propagate any fixpoint-inferred exports before
|
|
739
|
+
// `crossFile` disposes it downstream. Wrapped in try/catch so a cleanup
|
|
740
|
+
// failure never masks a real parse error; disposal stays with `crossFile`.
|
|
795
741
|
try {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
const allSequentialHeritage = [];
|
|
801
|
-
const cachedSequentialChunkFiles = [];
|
|
802
|
-
for (const chunkPaths of sequentialChunkPaths) {
|
|
803
|
-
const chunkContents = await readFileContents(repoPath, chunkPaths);
|
|
804
|
-
const chunkFiles = [];
|
|
805
|
-
for (const p of chunkPaths) {
|
|
806
|
-
const content = chunkContents.get(p);
|
|
807
|
-
if (content !== undefined)
|
|
808
|
-
chunkFiles.push({ path: p, content });
|
|
809
|
-
}
|
|
810
|
-
cachedSequentialChunkFiles.push(chunkFiles);
|
|
811
|
-
astCache = createASTCache(chunkFiles.length);
|
|
812
|
-
const sequentialHeritage = await extractExtractedHeritageFromFiles(chunkFiles, astCache);
|
|
813
|
-
for (const h of sequentialHeritage)
|
|
814
|
-
allSequentialHeritage.push(h);
|
|
815
|
-
astCache.clear();
|
|
816
|
-
}
|
|
817
|
-
const sequentialHeritageMap = allSequentialHeritage.length > 0
|
|
818
|
-
? buildHeritageMap(allSequentialHeritage, ctx, getHeritageStrategyForLanguage)
|
|
819
|
-
: undefined;
|
|
820
|
-
for (let chunkIdx = 0; chunkIdx < sequentialChunkPaths.length; chunkIdx++) {
|
|
821
|
-
const chunkFiles = cachedSequentialChunkFiles[chunkIdx];
|
|
822
|
-
astCache = createASTCache(chunkFiles.length);
|
|
823
|
-
const rubyHeritage = await processCalls(graph, chunkFiles, astCache, ctx, undefined, exportedTypeMap, undefined, undefined, undefined, sequentialHeritageMap, bindingAccumulator);
|
|
824
|
-
await processHeritage(graph, chunkFiles, astCache, ctx);
|
|
825
|
-
if (rubyHeritage.length > 0) {
|
|
826
|
-
await processHeritageFromExtracted(graph, rubyHeritage, ctx);
|
|
827
|
-
}
|
|
828
|
-
const chunkFetchCalls = await extractFetchCallsFromFiles(chunkFiles, astCache);
|
|
829
|
-
if (chunkFetchCalls.length > 0) {
|
|
830
|
-
for (const item of chunkFetchCalls)
|
|
831
|
-
allFetchCalls.push(item);
|
|
832
|
-
}
|
|
833
|
-
for (const f of chunkFiles) {
|
|
834
|
-
extractORMQueriesInline(f.path, f.content, allORMQueries);
|
|
835
|
-
}
|
|
836
|
-
astCache.clear();
|
|
837
|
-
cachedSequentialChunkFiles[chunkIdx] = [];
|
|
838
|
-
}
|
|
839
|
-
// Log resolution cache stats
|
|
840
|
-
if (isDev) {
|
|
841
|
-
const rcStats = ctx.getStats();
|
|
842
|
-
const total = rcStats.cacheHits + rcStats.cacheMisses;
|
|
843
|
-
const hitRate = total > 0 ? ((rcStats.cacheHits / total) * 100).toFixed(1) : '0';
|
|
844
|
-
logger.info(`🔍 Resolution cache: ${rcStats.cacheHits} hits, ${rcStats.cacheMisses} misses (${hitRate}% hit rate)`);
|
|
742
|
+
bindingAccumulator.finalize();
|
|
743
|
+
const enriched = enrichExportedTypeMap(bindingAccumulator, graph, exportedTypeMap);
|
|
744
|
+
if (isDev && enriched > 0) {
|
|
745
|
+
logger.info(`🔗 Worker TypeEnv enrichment: ${enriched} fixpoint-inferred exports added to ExportedTypeMap`);
|
|
845
746
|
}
|
|
846
747
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
astCache.clear();
|
|
851
|
-
// Run finalize + enrichment inside try/catch so a cleanup failure never
|
|
852
|
-
// masks the original fallback error. finalize must precede crossFile's
|
|
853
|
-
// dispose (U2) and enrichExportedTypeMap depends on finalized bindings.
|
|
854
|
-
try {
|
|
855
|
-
bindingAccumulator.finalize();
|
|
856
|
-
const enriched = enrichExportedTypeMap(bindingAccumulator, graph, exportedTypeMap);
|
|
857
|
-
if (isDev && enriched > 0) {
|
|
858
|
-
logger.info(`🔗 Worker TypeEnv enrichment: ${enriched} fixpoint-inferred exports added to ExportedTypeMap`);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
catch (enrichErr) {
|
|
862
|
-
if (isDev) {
|
|
863
|
-
logger.warn({ err: enrichErr.message }, 'Post-fallback finalize/enrich failed during cleanup:');
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
if (!hasSynthesized) {
|
|
868
|
-
const synthesized = synthesizeWildcardImportBindings(graph, ctx);
|
|
869
|
-
if (isDev && synthesized > 0) {
|
|
870
|
-
logger.info(`🔗 Synthesized ${synthesized} additional wildcard import bindings (Go/Ruby/C++/Swift/Python)`);
|
|
748
|
+
catch (enrichErr) {
|
|
749
|
+
if (isDev) {
|
|
750
|
+
logger.warn({ err: enrichErr.message }, 'Post-parse finalize/enrich failed during cleanup:');
|
|
871
751
|
}
|
|
872
752
|
}
|
|
873
753
|
// Worker-path enrichment: if exportedTypeMap is empty (e.g. the worker pool
|
|
@@ -877,22 +757,10 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
877
757
|
// crossFile receives a fully-populated map and never needs to mutate it for
|
|
878
758
|
// initial-graph enrichment.
|
|
879
759
|
if (exportedTypeMap.size === 0 && graph.nodeCount > 0) {
|
|
880
|
-
const graphExports = buildExportedTypeMapFromGraph(graph,
|
|
760
|
+
const graphExports = buildExportedTypeMapFromGraph(graph, model.symbols);
|
|
881
761
|
for (const [fp, exports] of graphExports)
|
|
882
762
|
exportedTypeMap.set(fp, exports);
|
|
883
763
|
}
|
|
884
|
-
allPathObjects.length = 0;
|
|
885
|
-
// Safe to reset importCtx caches here: `importCtx` (ImportResolutionContext)
|
|
886
|
-
// is a scratch workspace used only during import path resolution. The
|
|
887
|
-
// `resolutionContext` (`ctx`) returned below is a distinct object — it owns
|
|
888
|
-
// the fully-populated, post-parse `importMap` / `namedImportMap` /
|
|
889
|
-
// `packageMap` / `moduleAliasMap` / `model`, and never references
|
|
890
|
-
// `importCtx`. Cross-file re-resolution in cross-file-impl.ts consumes only
|
|
891
|
-
// `ctx` (via `processCalls`), so clearing the suffix index / resolveCache /
|
|
892
|
-
// normalizedFileList here cannot lose import matches downstream.
|
|
893
|
-
importCtx.resolveCache.clear();
|
|
894
|
-
importCtx.index = EMPTY_INDEX;
|
|
895
|
-
importCtx.normalizedFileList = [];
|
|
896
764
|
// FastAPI router-prefix resolution (cross-file).
|
|
897
765
|
//
|
|
898
766
|
// Workers emit two kinds of records per Python file:
|
|
@@ -1030,6 +898,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
1030
898
|
allDecoratorRoutes.push(dr);
|
|
1031
899
|
}
|
|
1032
900
|
}
|
|
901
|
+
logHeapProbe('parse-impl-return', `exportedTypeMap=${exportedTypeMap.size} parsedFiles=${allParsedFiles.length} nodes=${graph.nodeCount}`);
|
|
1033
902
|
return {
|
|
1034
903
|
exportedTypeMap,
|
|
1035
904
|
allFetchCalls,
|
|
@@ -1039,23 +908,15 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
1039
908
|
allToolDefs,
|
|
1040
909
|
allORMQueries,
|
|
1041
910
|
bindingAccumulator,
|
|
1042
|
-
|
|
1043
|
-
// Whether a worker pool was actually
|
|
1044
|
-
//
|
|
1045
|
-
//
|
|
911
|
+
model,
|
|
912
|
+
// Whether a worker pool was actually constructed for this run. False means
|
|
913
|
+
// no pool was needed: a warm all-cache-hit run replays cached worker output
|
|
914
|
+
// without spawning workers, or there were no parseable files.
|
|
1046
915
|
usedWorkerPool: workerPool !== undefined,
|
|
1047
|
-
// Surface the persistent scope cache so downstream phases
|
|
1048
|
-
// (scope-resolution) can skip re-parsing files that the
|
|
1049
|
-
// sequential path already parsed. Survives chunk boundaries; the
|
|
1050
|
-
// chunk-local `astCache` above is intentionally NOT exposed
|
|
1051
|
-
// because parse-impl clears it between chunks.
|
|
1052
|
-
scopeTreeCache,
|
|
1053
916
|
// Per-file ParsedFile artifacts produced by workers' calls to
|
|
1054
|
-
// `extractParsedFile`.
|
|
1055
|
-
//
|
|
1056
|
-
//
|
|
1057
|
-
// a re-extraction cache: when the file's ParsedFile is here,
|
|
1058
|
-
// scope-resolution skips its own `extractParsedFile` call.
|
|
917
|
+
// `extractParsedFile`. Consumed by scope-resolution as a re-extraction
|
|
918
|
+
// cache: when the file's ParsedFile is here, scope-resolution skips its own
|
|
919
|
+
// `extractParsedFile` call.
|
|
1059
920
|
parsedFiles: allParsedFiles,
|
|
1060
921
|
};
|
|
1061
922
|
}
|