gitnexus 1.6.6-rc.98 → 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 +247 -26
- 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
|
@@ -79,10 +79,29 @@ export class WorkerPoolDispatchError extends Error {
|
|
|
79
79
|
}
|
|
80
80
|
export class WorkerPoolInitializationError extends WorkerPoolDispatchError {
|
|
81
81
|
readinessFailures;
|
|
82
|
-
|
|
82
|
+
/** Pool's automatic classification of the startup crash (#1741). */
|
|
83
|
+
crashClass;
|
|
84
|
+
constructor(message, quarantinedPaths = [], readinessFailures = [], crashClass = 'transient-exhausted') {
|
|
83
85
|
super(message, quarantinedPaths);
|
|
84
86
|
this.name = 'WorkerPoolInitializationError';
|
|
85
87
|
this.readinessFailures = readinessFailures;
|
|
88
|
+
this.crashClass = crashClass;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Thrown when a caller asks GitNexus to parse without the worker pool —
|
|
93
|
+
* `--workers 0`, `GITNEXUS_WORKER_POOL_SIZE=0`, or `skipWorkers: true`.
|
|
94
|
+
*
|
|
95
|
+
* GitNexus no longer has a sequential parser: the worker pool (with its
|
|
96
|
+
* quarantine + respawn/recycle + circuit-breaker resilience) is the SOLE
|
|
97
|
+
* parse path. These channels used to select an in-process fallback; they are
|
|
98
|
+
* now hard configuration errors so the operator gets an actionable message
|
|
99
|
+
* instead of silently parsing through a (deleted) slower path.
|
|
100
|
+
*/
|
|
101
|
+
export class WorkerPoolDisabledError extends Error {
|
|
102
|
+
constructor(message) {
|
|
103
|
+
super(message);
|
|
104
|
+
this.name = 'WorkerPoolDisabledError';
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
107
|
/**
|
|
@@ -117,6 +136,89 @@ const WORKER_READY_TIMEOUT_MS = 5_000;
|
|
|
117
136
|
* `GITNEXUS_WORKER_POOL_SIZE` or `--workers <N>`.
|
|
118
137
|
*/
|
|
119
138
|
const DEFAULT_POOL_SIZE_CAP = 16;
|
|
139
|
+
// ── Self-healing startup restart policy (#1741) ──────────────────────────────
|
|
140
|
+
// A worker that crashes during top-of-script init (broken native binding, bad
|
|
141
|
+
// import) is retried a BOUNDED number of times with jittered backoff before
|
|
142
|
+
// its slot is dropped, so a transient blip self-heals with no operator
|
|
143
|
+
// intervention. The bound is the whole point of #1741: recovery must never
|
|
144
|
+
// become a silent, unbounded "stuck" run. When the budget is exhausted (or a
|
|
145
|
+
// deterministic crash-loop is detected), the slot is dropped; if every slot is
|
|
146
|
+
// dropped the first dispatch fails fast with the captured cause.
|
|
147
|
+
/** Retries beyond the first attempt, per slot, to bring a startup worker ready. */
|
|
148
|
+
const STARTUP_RESTART_BUDGET = 2;
|
|
149
|
+
const RESTART_BACKOFF_BASE_MS = 250;
|
|
150
|
+
const RESTART_BACKOFF_CAP_MS = 2_000;
|
|
151
|
+
/**
|
|
152
|
+
* When this many freshly-spawned workers crash with the SAME crash signature
|
|
153
|
+
* before ANY worker reaches the `{type:'ready'}` handshake, the failure is
|
|
154
|
+
* deterministic (the #1741 missing-binding case: every worker prints a
|
|
155
|
+
* byte-identical native-binding stack). The pool stops retrying immediately
|
|
156
|
+
* instead of burning every slot's budget, and fails fast with the cause.
|
|
157
|
+
*/
|
|
158
|
+
const DETERMINISTIC_STARTUP_FINGERPRINT_THRESHOLD = 2;
|
|
159
|
+
/**
|
|
160
|
+
* Capped exponential backoff with FULL jitter (AWS "Exponential Backoff And
|
|
161
|
+
* Jitter"): random(0, min(CAP, BASE·2^attempt)). Full jitter de-synchronizes
|
|
162
|
+
* the N workers that crash near-simultaneously on a shared startup fault so
|
|
163
|
+
* their respawns don't re-storm in lockstep (Google SRE thundering herd).
|
|
164
|
+
*/
|
|
165
|
+
function startupBackoffMs(attempt) {
|
|
166
|
+
const ceil = Math.min(RESTART_BACKOFF_CAP_MS, RESTART_BACKOFF_BASE_MS * 2 ** attempt);
|
|
167
|
+
return Math.floor(Math.random() * (ceil + 1));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Sleep used between startup self-heal retries. The timer is intentionally NOT
|
|
171
|
+
* `unref`'d: a pending retry is necessary work, so it must keep the event loop
|
|
172
|
+
* alive long enough to actually respawn — otherwise a pool whose only live work
|
|
173
|
+
* is a startup backoff could let the process exit mid-recovery (#1741). To
|
|
174
|
+
* avoid wedging shutdown, the timer registers a cancel function in `pending`;
|
|
175
|
+
* `terminate()` invokes those cancels to `clearTimeout` and resolve early, and
|
|
176
|
+
* a normally-fired timer removes its own cancel. `aborted()` is checked once up
|
|
177
|
+
* front; the CALLER re-checks after wake (it owns the terminated/deterministic
|
|
178
|
+
* decision) — this function does not itself re-evaluate abort on wake.
|
|
179
|
+
*/
|
|
180
|
+
function abortableSleep(ms, aborted, pending) {
|
|
181
|
+
return new Promise((resolve) => {
|
|
182
|
+
if (ms <= 0 || aborted()) {
|
|
183
|
+
resolve();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// `cancel` is registered so terminate() can clear a pending backoff; it is
|
|
187
|
+
// also the timer's own callback, so a normally-fired sleep self-deregisters.
|
|
188
|
+
const cancel = () => {
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
pending.delete(cancel);
|
|
191
|
+
resolve();
|
|
192
|
+
};
|
|
193
|
+
const timer = setTimeout(cancel, ms);
|
|
194
|
+
pending.add(cancel);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Normalize a worker crash message into a stable signature so two instances of
|
|
199
|
+
* the SAME deterministic crash compare equal while unrelated crashes don't.
|
|
200
|
+
* Strips hex addresses, digit runs (pids / line numbers / timestamps) and
|
|
201
|
+
* absolute paths. Best-effort by design: the deterministic classification's
|
|
202
|
+
* correctness rests on the STRUCTURAL signal (zero workers ever ready + startup
|
|
203
|
+
* budget exhausted), so an imperfect signature only changes how fast the
|
|
204
|
+
* short-circuit fires, never whether the pool ultimately fails fast. Even a
|
|
205
|
+
* stderr-less crash normalizes its "exited with code N" message to a stable
|
|
206
|
+
* key, so the empty-stderr timing case still groups.
|
|
207
|
+
*
|
|
208
|
+
* @internal Exported for unit tests; production callers are in this module.
|
|
209
|
+
*/
|
|
210
|
+
export function crashSignature(message) {
|
|
211
|
+
return (message
|
|
212
|
+
.replace(/0x[0-9a-fA-F]+/g, '0xADDR') // 0x-prefixed addresses
|
|
213
|
+
// Windows backslash paths (optional drive letter), e.g. C:\Users\ci\Temp\w-7f3a.js
|
|
214
|
+
.replace(/(?:[A-Za-z]:)?(?:\\[^\s\\'"]+)+/g, '\\PATH')
|
|
215
|
+
.replace(/(?:\/[^\s:'"]+)+/g, '/PATH') // POSIX paths
|
|
216
|
+
.replace(/\b[0-9a-fA-F]{6,}\b/g, 'HEX') // bare hex runs (ASLR addrs / backtrace tokens)
|
|
217
|
+
.replace(/[0-9]+/g, 'N') // pids / line numbers / exit codes / timestamps
|
|
218
|
+
.replace(/\s+/g, ' ')
|
|
219
|
+
.trim()
|
|
220
|
+
.slice(0, 300));
|
|
221
|
+
}
|
|
120
222
|
function positiveInteger(value) {
|
|
121
223
|
const parsed = typeof value === 'string' ? Number(value) : value;
|
|
122
224
|
return typeof parsed === 'number' && Number.isFinite(parsed) && parsed > 0
|
|
@@ -152,12 +254,37 @@ export function resolveWorkerPoolOptions(options = {}, poolSize) {
|
|
|
152
254
|
Math.max(DEFAULT_CONSECUTIVE_FAILURE_THRESHOLD_FLOOR, poolSize ?? 0),
|
|
153
255
|
};
|
|
154
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* The pool size requested via the `GITNEXUS_WORKER_POOL_SIZE` env var, or
|
|
259
|
+
* `undefined` when unset, empty/whitespace, or invalid. Module-internal sizing
|
|
260
|
+
* reader consumed by {@link resolveAutoPoolSize} (the env override) and
|
|
261
|
+
* {@link workerPoolDisabledByEnv} (the disabled-channel check). Reads only —
|
|
262
|
+
* never mutates `process.env`. Empty/whitespace is treated as *unset* (falls
|
|
263
|
+
* through to the auto formula), not as 0 — an empty assignment (`export
|
|
264
|
+
* GITNEXUS_WORKER_POOL_SIZE=`) is an accident, not a request for zero workers;
|
|
265
|
+
* only a literal `0` disables the pool.
|
|
266
|
+
*/
|
|
267
|
+
function envWorkerPoolSize() {
|
|
268
|
+
const raw = process.env.GITNEXUS_WORKER_POOL_SIZE;
|
|
269
|
+
if (raw === undefined || raw.trim() === '')
|
|
270
|
+
return undefined;
|
|
271
|
+
return nonNegativeInteger(raw);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* True when the operator set `GITNEXUS_WORKER_POOL_SIZE=0` — the env-channel
|
|
275
|
+
* equivalent of `--workers 0`. The parse phase consults this (only when no
|
|
276
|
+
* explicit `--workers <N>` was passed) and HARD-ERRORS: sequential parsing was
|
|
277
|
+
* removed, so a disabled pool is an actionable configuration error, not a
|
|
278
|
+
* silent fallback. An explicit positive `--workers N` always wins.
|
|
279
|
+
*/
|
|
280
|
+
export function workerPoolDisabledByEnv() {
|
|
281
|
+
return envWorkerPoolSize() === 0;
|
|
282
|
+
}
|
|
155
283
|
/**
|
|
156
284
|
* Resolve the auto-default worker pool size when no explicit `poolSize`
|
|
157
285
|
* arg is passed to `createWorkerPool`. Precedence:
|
|
158
286
|
*
|
|
159
|
-
* 1. `GITNEXUS_WORKER_POOL_SIZE` env var (operator override
|
|
160
|
-
* `--workers <N>` on the CLI).
|
|
287
|
+
* 1. `GITNEXUS_WORKER_POOL_SIZE` env var (operator override).
|
|
161
288
|
* 2. `os.cpus().length - 1`, clamped to `[1, DEFAULT_POOL_SIZE_CAP]`.
|
|
162
289
|
*
|
|
163
290
|
* The cap exists because past ~16 workers the main-thread merge /
|
|
@@ -170,7 +297,7 @@ export function resolveWorkerPoolOptions(options = {}, poolSize) {
|
|
|
170
297
|
* on the env / default.
|
|
171
298
|
*/
|
|
172
299
|
export function resolveAutoPoolSize() {
|
|
173
|
-
const envOverride =
|
|
300
|
+
const envOverride = envWorkerPoolSize();
|
|
174
301
|
if (envOverride !== undefined)
|
|
175
302
|
return envOverride;
|
|
176
303
|
// Prefer os.availableParallelism (Node 18.14+) so cgroup CPU limits
|
|
@@ -184,6 +311,69 @@ export function resolveAutoPoolSize() {
|
|
|
184
311
|
const cores = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
185
312
|
return Math.min(DEFAULT_POOL_SIZE_CAP, Math.max(1, cores - 1));
|
|
186
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Max characters of a worker's stderr retained for crash diagnostics. A
|
|
316
|
+
* native-binding load failure or a top-of-script throw prints a stack to
|
|
317
|
+
* stderr; we keep the tail so `waitForWorkerReady` can attach the real
|
|
318
|
+
* reason to its rejection instead of the generic "did not report ready".
|
|
319
|
+
*/
|
|
320
|
+
const WORKER_STDERR_TAIL_LIMIT = 4000;
|
|
321
|
+
/**
|
|
322
|
+
* Per-worker captured stderr tail. Populated only for workers spawned with
|
|
323
|
+
* `{ stderr: true }` (the production factory below). Test-injected workers
|
|
324
|
+
* via `workerFactory` typically inherit the parent's stderr and have no
|
|
325
|
+
* `worker.stderr` stream — those are simply skipped (empty tail). A WeakMap
|
|
326
|
+
* so the buffer is released when the worker is GC'd.
|
|
327
|
+
*/
|
|
328
|
+
const workerStderrTails = new WeakMap();
|
|
329
|
+
/**
|
|
330
|
+
* Tee a worker's stderr into a bounded in-memory tail (for surfacing the
|
|
331
|
+
* real crash on a startup failure) while still mirroring it to the parent
|
|
332
|
+
* process's stderr — preserving the live-diagnostics behavior workers had
|
|
333
|
+
* when they inherited stderr, before `{ stderr: true }` redirected it to a
|
|
334
|
+
* stream. No-op when the worker has no `stderr` stream (test factories).
|
|
335
|
+
*/
|
|
336
|
+
function captureWorkerStderr(worker) {
|
|
337
|
+
const stream = worker.stderr;
|
|
338
|
+
if (!stream)
|
|
339
|
+
return;
|
|
340
|
+
const buf = { text: '' };
|
|
341
|
+
workerStderrTails.set(worker, buf);
|
|
342
|
+
stream.on('data', (chunk) => {
|
|
343
|
+
const s = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
344
|
+
process.stderr.write(s);
|
|
345
|
+
buf.text = (buf.text + s).slice(-WORKER_STDERR_TAIL_LIMIT);
|
|
346
|
+
});
|
|
347
|
+
// A stderr stream error must never crash the pool.
|
|
348
|
+
stream.on('error', () => undefined);
|
|
349
|
+
}
|
|
350
|
+
/** Captured stderr tail for a worker, trimmed; '' when nothing was captured. */
|
|
351
|
+
function workerStderrTail(worker) {
|
|
352
|
+
return workerStderrTails.get(worker)?.text.trim() ?? '';
|
|
353
|
+
}
|
|
354
|
+
/** Append the worker's captured stderr to a readiness-failure message. */
|
|
355
|
+
function withStderr(worker, message) {
|
|
356
|
+
const tail = workerStderrTail(worker);
|
|
357
|
+
return tail ? `${message}. Worker stderr:\n${tail}` : message;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Build a worker-death reason string that carries the worker-side stack when one
|
|
361
|
+
* is available (#2068). The stack is appended AFTER the `Worker N error: <msg>`
|
|
362
|
+
* prefix so every prefix/substring consumer downstream — recoverAndResume →
|
|
363
|
+
* handleWorkerDeath → the circuit-breaker `WorkerPoolDispatchError` message, and
|
|
364
|
+
* the tests that regex-match those — keeps working unchanged, while the operator
|
|
365
|
+
* now gets the real frame instead of a bare one-liner. The stack's first line is
|
|
366
|
+
* normally the message itself; keeping both is harmless and the indented block
|
|
367
|
+
* scans cleanly in a log. The stack is capped at WORKER_STDERR_TAIL_LIMIT,
|
|
368
|
+
* mirroring the sibling stderr-tail bound, so a pathological error type (or a
|
|
369
|
+
* raised `Error.stackTraceLimit`) can't bloat the death reason. `stack` is
|
|
370
|
+
* `undefined` for an older worker build (or a thrown non-Error), in which case
|
|
371
|
+
* the reason is exactly the prior message-only form.
|
|
372
|
+
*/
|
|
373
|
+
function workerErrorReason(workerIndex, message, stack) {
|
|
374
|
+
const base = `Worker ${workerIndex} error: ${message}`;
|
|
375
|
+
return stack ? `${base}\n worker stack:\n${stack.slice(0, WORKER_STDERR_TAIL_LIMIT)}` : base;
|
|
376
|
+
}
|
|
187
377
|
/**
|
|
188
378
|
* Wait for a freshly-spawned replacement worker to emit the
|
|
189
379
|
* `{type:'ready'}` handshake from `parse-worker.ts` before treating its
|
|
@@ -220,22 +410,24 @@ function waitForWorkerReady(worker) {
|
|
|
220
410
|
};
|
|
221
411
|
const onError = (err) => {
|
|
222
412
|
cleanup();
|
|
223
|
-
|
|
413
|
+
// The 'error' event carries the real top-of-script exception; enrich it
|
|
414
|
+
// with the worker's stderr tail (native-binding stacks land there).
|
|
415
|
+
reject(new Error(withStderr(worker, err.message)));
|
|
224
416
|
};
|
|
225
417
|
const onExit = (code) => {
|
|
226
418
|
cleanup();
|
|
227
|
-
reject(new Error(`Replacement worker exited with code ${code} before reporting ready`));
|
|
419
|
+
reject(new Error(withStderr(worker, `Replacement worker exited with code ${code} before reporting ready`)));
|
|
228
420
|
};
|
|
229
421
|
const onMessageError = (err) => {
|
|
230
422
|
cleanup();
|
|
231
|
-
reject(new Error(`Replacement worker emitted messageerror before reporting ready: ${err.message}`));
|
|
423
|
+
reject(new Error(withStderr(worker, `Replacement worker emitted messageerror before reporting ready: ${err.message}`)));
|
|
232
424
|
};
|
|
233
425
|
// `timer` is declared after `cleanup` so the cleanup closure can reference
|
|
234
426
|
// it. The const is reached before any handler attaches below, so no TDZ
|
|
235
427
|
// access can fire from the listeners.
|
|
236
428
|
const timer = setTimeout(() => {
|
|
237
429
|
cleanup();
|
|
238
|
-
reject(new Error(`Replacement worker did not report ready within ${WORKER_READY_TIMEOUT_MS}ms — likely crashed during top-of-script init`));
|
|
430
|
+
reject(new Error(withStderr(worker, `Replacement worker did not report ready within ${WORKER_READY_TIMEOUT_MS}ms — likely crashed during top-of-script init`)));
|
|
239
431
|
}, WORKER_READY_TIMEOUT_MS);
|
|
240
432
|
worker.on('message', onMessage);
|
|
241
433
|
worker.once('error', onError);
|
|
@@ -275,7 +467,7 @@ function inFlightExcludePath(job, lastProgress) {
|
|
|
275
467
|
const path = itemPath(job.items[lastProgress]);
|
|
276
468
|
return path ? [path] : [];
|
|
277
469
|
}
|
|
278
|
-
function createJobs(items, maxItems, maxBytes, timeoutMs) {
|
|
470
|
+
function createJobs(items, maxItems, maxBytes, timeoutMs, chunkHash) {
|
|
279
471
|
const jobs = [];
|
|
280
472
|
let startIndex = 0;
|
|
281
473
|
let batch = [];
|
|
@@ -289,6 +481,7 @@ function createJobs(items, maxItems, maxBytes, timeoutMs) {
|
|
|
289
481
|
estimatedBytes: batchBytes,
|
|
290
482
|
attempt: 0,
|
|
291
483
|
splitDepth: 0,
|
|
484
|
+
chunkHash,
|
|
292
485
|
timeoutMs,
|
|
293
486
|
cumulativeTimeoutMs: timeoutMs,
|
|
294
487
|
});
|
|
@@ -340,7 +533,31 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
340
533
|
}
|
|
341
534
|
const size = poolSize ?? resolveAutoPoolSize();
|
|
342
535
|
const poolOptions = resolveWorkerPoolOptions(options, size);
|
|
343
|
-
|
|
536
|
+
// Production factory spawns with `{ stderr: true }` so a worker's crash
|
|
537
|
+
// output is redirected to a `worker.stderr` stream we can tee + capture
|
|
538
|
+
// (see captureWorkerStderr) and attach to readiness-failure messages —
|
|
539
|
+
// instead of the generic "did not report ready" that hid the real cause
|
|
540
|
+
// in #1741. Test factories (workerFactory) are used verbatim.
|
|
541
|
+
// Bake the (immutable) ParsedFile store path into the factory closure so it
|
|
542
|
+
// reaches EVERY spawned worker — including respawns, which reuse this same
|
|
543
|
+
// factory — via `workerData`, read once at worker init. The `(url) => Worker`
|
|
544
|
+
// signature is unchanged so the zero-arg test factories keep working.
|
|
545
|
+
const parsedFileStoreStoragePath = options?.parsedFileStoreStoragePath;
|
|
546
|
+
const durableParsedFileStoragePath = options?.durableParsedFileStoragePath;
|
|
547
|
+
const workerStoreData = parsedFileStoreStoragePath || durableParsedFileStoragePath
|
|
548
|
+
? { parsedFileStoreStoragePath, durableParsedFileStoragePath }
|
|
549
|
+
: undefined;
|
|
550
|
+
const spawnWorker = options?.workerFactory ??
|
|
551
|
+
((url) => new Worker(url, {
|
|
552
|
+
stderr: true,
|
|
553
|
+
workerData: workerStoreData,
|
|
554
|
+
}));
|
|
555
|
+
/** Spawn + wire stderr capture in one step (used by all spawn sites). */
|
|
556
|
+
const spawnAndCapture = (url) => {
|
|
557
|
+
const worker = spawnWorker(url);
|
|
558
|
+
captureWorkerStderr(worker);
|
|
559
|
+
return worker;
|
|
560
|
+
};
|
|
344
561
|
const workers = new Array(size);
|
|
345
562
|
const retiredWorkers = new Set();
|
|
346
563
|
const respawnCount = new Array(size).fill(0);
|
|
@@ -369,6 +586,9 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
369
586
|
const slotGenerations = new Array(size).fill(0);
|
|
370
587
|
let poolBroken = false;
|
|
371
588
|
let poolFailure;
|
|
589
|
+
// Set by `terminate()` (below). Also read by the self-healing startup loop so
|
|
590
|
+
// a terminate during startup aborts pending backoff/retries (#1741).
|
|
591
|
+
let terminated = false;
|
|
372
592
|
const terminateTrackedWorkers = async (liveWorkers) => {
|
|
373
593
|
const retired = Array.from(retiredWorkers);
|
|
374
594
|
await Promise.all([
|
|
@@ -378,42 +598,97 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
378
598
|
retiredWorkers.clear();
|
|
379
599
|
};
|
|
380
600
|
for (let i = 0; i < size; i++) {
|
|
381
|
-
workers[i] =
|
|
601
|
+
workers[i] = spawnAndCapture(workerUrl);
|
|
382
602
|
activeSlots.add(i);
|
|
383
603
|
}
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// missing dependency) would only be noticed at the first dispatch's
|
|
390
|
-
// 30s idle timeout, vs the 5s WORKER_READY_TIMEOUT_MS bound that
|
|
391
|
-
// replacements enjoy.
|
|
604
|
+
// ── Self-healing startup readiness (#1741) ────────────────────────────────
|
|
605
|
+
// Bring every initial slot to readiness with a BOUNDED, jittered retry loop
|
|
606
|
+
// instead of dropping it on the first crash. This symmetrizes the gate with
|
|
607
|
+
// the runtime `replaceWorker` path (which already respawns a crashed slot),
|
|
608
|
+
// and adds genuine self-healing at startup:
|
|
392
609
|
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
610
|
+
// - TRANSIENT crash (a one-off OS hiccup / fork throttle): the slot is
|
|
611
|
+
// respawned after jittered backoff and retried, up to STARTUP_RESTART_BUDGET
|
|
612
|
+
// — so a blip heals itself with no operator intervention.
|
|
613
|
+
// - DETERMINISTIC crash-loop (every worker dies with the SAME signature
|
|
614
|
+
// before any reaches ready — the #1741 missing-binding case): detected via
|
|
615
|
+
// `crashSignature` and short-circuited immediately, so the pool gives up in
|
|
616
|
+
// ~1s rather than burning every slot's budget.
|
|
617
|
+
//
|
|
618
|
+
// When the loop exhausts, the slot is dropped from `activeSlots`. If EVERY
|
|
619
|
+
// slot is dropped, the first dispatch throws WorkerPoolInitializationError
|
|
620
|
+
// carrying the captured crash cause + classification — never a silent hang.
|
|
621
|
+
// Correctness of the deterministic short-circuit rests on the STRUCTURAL
|
|
622
|
+
// signal (zero workers ever ready + budget exhausted), not on signature
|
|
623
|
+
// matching alone: a missed match only costs a few seconds of extra retrying.
|
|
624
|
+
// Deterministic crash-loop detection (#1741). A crash counts toward
|
|
625
|
+
// "deterministic" ONLY after its signature reproduces across a respawn on the
|
|
626
|
+
// same slot — so every slot is guaranteed at least one self-heal attempt and
|
|
627
|
+
// a simultaneous attempt-0 crash storm (e.g. transient `spawn EAGAIN` under
|
|
628
|
+
// fork pressure) cannot be misclassified as deterministic. We short-circuit
|
|
629
|
+
// once enough DISTINCT slots have each reproduced: ≥2 normally, or 1 for a
|
|
630
|
+
// size-1 pool. Until then the structural floor (every slot exhausts its
|
|
631
|
+
// budget) still bounds the worst case, so a missed match only costs retries.
|
|
632
|
+
const lastStartupSignature = new Map();
|
|
633
|
+
const reproducedStartupSlots = new Set();
|
|
634
|
+
const deterministicSlotThreshold = Math.min(DETERMINISTIC_STARTUP_FINGERPRINT_THRESHOLD, size);
|
|
635
|
+
let deterministicStartupDetected = false;
|
|
636
|
+
let anyWorkerReachedReady = false;
|
|
637
|
+
// Cancel functions for in-flight startup backoffs (see abortableSleep). The
|
|
638
|
+
// backoff timer is ref'd so a retry actually runs; terminate() invokes these
|
|
639
|
+
// to clear pending backoffs and resolve their sleeps so the slot loops wake,
|
|
640
|
+
// see `terminated`, and give up — instead of the process staying pinned for
|
|
641
|
+
// the backoff cap after terminate (#1741).
|
|
642
|
+
const pendingStartupTimers = new Set();
|
|
643
|
+
const bringSlotReady = async (i) => {
|
|
644
|
+
for (let attempt = 0;; attempt++) {
|
|
645
|
+
const worker = workers[i];
|
|
646
|
+
if (!worker)
|
|
647
|
+
return; // terminated mid-startup
|
|
648
|
+
try {
|
|
649
|
+
await waitForWorkerReady(worker);
|
|
650
|
+
anyWorkerReachedReady = true;
|
|
651
|
+
return; // ready — slot stays in activeSlots
|
|
652
|
+
}
|
|
653
|
+
catch (err) {
|
|
654
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
655
|
+
const sig = crashSignature(msg);
|
|
656
|
+
// Same signature as this slot's previous attempt => it survived a
|
|
657
|
+
// respawn, so retrying this slot is futile. (First crash has no prior
|
|
658
|
+
// signature, so attempt 0 never counts — every slot self-heals once.)
|
|
659
|
+
if (lastStartupSignature.get(i) === sig)
|
|
660
|
+
reproducedStartupSlots.add(i);
|
|
661
|
+
lastStartupSignature.set(i, sig);
|
|
662
|
+
if (!anyWorkerReachedReady && reproducedStartupSlots.size >= deterministicSlotThreshold) {
|
|
663
|
+
deterministicStartupDetected = true;
|
|
664
|
+
}
|
|
665
|
+
await worker.terminate().catch(() => undefined);
|
|
666
|
+
workers[i] = undefined;
|
|
667
|
+
const giveUp = terminated || deterministicStartupDetected || attempt >= STARTUP_RESTART_BUDGET;
|
|
668
|
+
if (giveUp) {
|
|
669
|
+
initialReadinessFailures.push(msg);
|
|
670
|
+
activeSlots.delete(i);
|
|
671
|
+
logger.warn({ workerIndex: i, attempt, err: msg, deterministic: deterministicStartupDetected }, deterministicStartupDetected
|
|
672
|
+
? `Worker ${i} hit a deterministic startup crash-loop; dropping slot without further retries.`
|
|
673
|
+
: `Worker ${i} did not report ready after ${attempt + 1} attempt(s); dropping slot.`);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
// Transient: jittered backoff, then respawn the slot and retry.
|
|
677
|
+
await abortableSleep(startupBackoffMs(attempt), () => terminated || deterministicStartupDetected, pendingStartupTimers);
|
|
678
|
+
if (terminated || deterministicStartupDetected) {
|
|
679
|
+
initialReadinessFailures.push(msg);
|
|
680
|
+
activeSlots.delete(i);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
logger.warn({ workerIndex: i, attempt: attempt + 1 }, `Worker ${i} crashed during startup; respawning slot (self-heal attempt ${attempt + 1}/${STARTUP_RESTART_BUDGET}).`);
|
|
684
|
+
workers[i] = spawnAndCapture(workerUrl);
|
|
685
|
+
}
|
|
414
686
|
}
|
|
415
|
-
}
|
|
416
|
-
|
|
687
|
+
};
|
|
688
|
+
// First dispatch awaits this; it settles every slot's bounded retry loop in
|
|
689
|
+
// parallel and drops the unrecoverable ones before any dispatch can fire.
|
|
690
|
+
const initialReadyGate = Promise.allSettled(workers.map((_, i) => bringSlotReady(i))).then(() => undefined);
|
|
691
|
+
const dispatch = async (items, onProgress, chunkHash) => {
|
|
417
692
|
// Await the initial-spawn readiness gate (F13). On first dispatch
|
|
418
693
|
// this blocks for up to WORKER_READY_TIMEOUT_MS while every initial
|
|
419
694
|
// worker's `{type:'ready'}` handshake is checked; on subsequent
|
|
@@ -434,7 +709,13 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
434
709
|
const detail = initialReadinessFailures.length > 0
|
|
435
710
|
? ` after initial ready handshake: ${initialReadinessFailures.join('; ')}`
|
|
436
711
|
: '';
|
|
437
|
-
|
|
712
|
+
// The bounded self-heal exhausted (or short-circuited a deterministic
|
|
713
|
+
// crash-loop). Classify automatically so the caller renders the real
|
|
714
|
+
// cause without consulting any operator flag (#1741).
|
|
715
|
+
const crashClass = deterministicStartupDetected
|
|
716
|
+
? 'deterministic-startup'
|
|
717
|
+
: 'transient-exhausted';
|
|
718
|
+
throw new WorkerPoolInitializationError(`Worker pool has no active workers${detail}`, [], initialReadinessFailures, crashClass);
|
|
438
719
|
}
|
|
439
720
|
// Layer 3: filter out quarantined paths so a known-bad file never reaches
|
|
440
721
|
// a worker again this pool lifetime. The caller queries
|
|
@@ -448,7 +729,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
448
729
|
}
|
|
449
730
|
if (dispatchableItems.length === 0)
|
|
450
731
|
return [];
|
|
451
|
-
const jobs = createJobs(dispatchableItems, poolOptions.subBatchSize, poolOptions.subBatchMaxBytes, poolOptions.subBatchIdleTimeoutMs);
|
|
732
|
+
const jobs = createJobs(dispatchableItems, poolOptions.subBatchSize, poolOptions.subBatchMaxBytes, poolOptions.subBatchIdleTimeoutMs, chunkHash);
|
|
452
733
|
return new Promise((resolve, reject) => {
|
|
453
734
|
const results = [];
|
|
454
735
|
const inFlightProgress = new Array(size).fill(0);
|
|
@@ -549,7 +830,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
549
830
|
await removeWorkerFromSlot(workerIndex, mode, reason);
|
|
550
831
|
if (stopped)
|
|
551
832
|
return false;
|
|
552
|
-
const replacement =
|
|
833
|
+
const replacement = spawnAndCapture(workerUrl);
|
|
553
834
|
try {
|
|
554
835
|
await waitForWorkerReady(replacement);
|
|
555
836
|
}
|
|
@@ -663,6 +944,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
663
944
|
estimatedBytes: filtered.reduce((sum, item) => sum + estimateItemBytes(item), 0),
|
|
664
945
|
attempt: job.attempt,
|
|
665
946
|
splitDepth: job.splitDepth,
|
|
947
|
+
chunkHash: job.chunkHash,
|
|
666
948
|
timeoutMs: job.timeoutMs,
|
|
667
949
|
cumulativeTimeoutMs: job.cumulativeTimeoutMs,
|
|
668
950
|
});
|
|
@@ -753,6 +1035,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
753
1035
|
estimatedBytes: firstItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
|
|
754
1036
|
attempt: job.attempt,
|
|
755
1037
|
splitDepth: job.splitDepth + 1,
|
|
1038
|
+
chunkHash: job.chunkHash,
|
|
756
1039
|
timeoutMs: nextTimeout,
|
|
757
1040
|
cumulativeTimeoutMs: nextCumulative,
|
|
758
1041
|
};
|
|
@@ -762,6 +1045,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
762
1045
|
estimatedBytes: secondItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
|
|
763
1046
|
attempt: job.attempt,
|
|
764
1047
|
splitDepth: job.splitDepth + 1,
|
|
1048
|
+
chunkHash: job.chunkHash,
|
|
765
1049
|
timeoutMs: nextTimeout,
|
|
766
1050
|
cumulativeTimeoutMs: nextCumulative,
|
|
767
1051
|
};
|
|
@@ -1064,12 +1348,15 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
1064
1348
|
else if (msg.type === 'sub-batch-done') {
|
|
1065
1349
|
waitingForFlush = true;
|
|
1066
1350
|
resetIdleTimer();
|
|
1067
|
-
|
|
1351
|
+
// Carry the chunk hash on the flush so the worker can write a
|
|
1352
|
+
// durable, content-addressed ParsedFile shard (warm-cache reuse)
|
|
1353
|
+
// at the flush boundary where `accumulated.parsedFiles` is complete.
|
|
1354
|
+
worker.postMessage({ type: 'flush', chunkHash: job.chunkHash });
|
|
1068
1355
|
}
|
|
1069
1356
|
else if (msg.type === 'error') {
|
|
1070
1357
|
settled = true;
|
|
1071
1358
|
cleanup();
|
|
1072
|
-
void recoverAndResume(
|
|
1359
|
+
void recoverAndResume(workerErrorReason(workerIndex, msg.error, msg.errorStack), resolveExcludePaths());
|
|
1073
1360
|
}
|
|
1074
1361
|
else if (msg.type === 'result') {
|
|
1075
1362
|
if (!waitingForFlush) {
|
|
@@ -1110,7 +1397,12 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
1110
1397
|
if (!settled) {
|
|
1111
1398
|
settled = true;
|
|
1112
1399
|
cleanup();
|
|
1113
|
-
|
|
1400
|
+
// The Node 'error' event fires on an UNCAUGHT worker throw (one that
|
|
1401
|
+
// escaped the worker's own try/catch, or an async rejection). Unlike
|
|
1402
|
+
// the `{type:'error'}` message, the event delivers a real Error whose
|
|
1403
|
+
// `.stack` is the worker-side frame — carry it so the surfaced reason
|
|
1404
|
+
// points at the actual failure site, not just `err.message` (#2068).
|
|
1405
|
+
void recoverAndResume(workerErrorReason(workerIndex, err.message, err.stack), resolveExcludePaths());
|
|
1114
1406
|
}
|
|
1115
1407
|
};
|
|
1116
1408
|
const exitHandler = (code) => {
|
|
@@ -1165,9 +1457,13 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
1165
1457
|
runWorker(slotIndex);
|
|
1166
1458
|
});
|
|
1167
1459
|
};
|
|
1168
|
-
let terminated = false;
|
|
1169
1460
|
const terminate = async () => {
|
|
1170
1461
|
terminated = true;
|
|
1462
|
+
// Cancel any in-flight startup backoff so its ref'd timer doesn't keep the
|
|
1463
|
+
// event loop alive after terminate; each cancel resolves the awaiting sleep
|
|
1464
|
+
// and the slot loop then sees `terminated` and gives up (#1741).
|
|
1465
|
+
for (const cancel of [...pendingStartupTimers])
|
|
1466
|
+
cancel();
|
|
1171
1467
|
// `.catch(() => undefined)` per-worker matches every other terminate
|
|
1172
1468
|
// site in this file. Without it, a hung/OOM-killed worker's terminate
|
|
1173
1469
|
// rejection escapes `Promise.all` and replaces the original pipeline
|
|
@@ -1190,6 +1486,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
|
|
|
1190
1486
|
quarantined: quarantine.size,
|
|
1191
1487
|
poolBroken,
|
|
1192
1488
|
terminated,
|
|
1489
|
+
pendingStartupTimers: pendingStartupTimers.size,
|
|
1193
1490
|
slotGenerations: slotGenerations.slice(),
|
|
1194
1491
|
}),
|
|
1195
1492
|
};
|
|
@@ -14,6 +14,21 @@
|
|
|
14
14
|
import fs from 'fs/promises';
|
|
15
15
|
import { createWriteStream } from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
|
+
import { parseTruthyEnv } from '../ingestion/utils/env.js';
|
|
18
|
+
/**
|
|
19
|
+
* Deterministic output ordering — optional (out-of-core / windowed-resolve
|
|
20
|
+
* enabler). When `GITNEXUS_SORT_GRAPH_OUTPUT` is set, nodes and relationships
|
|
21
|
+
* are emitted sorted by their (unique, dedup-key) graph `id` rather than in
|
|
22
|
+
* graph-insertion order, making the CSV a pure function of the graph's node/edge
|
|
23
|
+
* SET instead of of emit order. Default off returns the iterator untouched, so
|
|
24
|
+
* the bytes are identical to today. With it on, a windowed/out-of-core emit
|
|
25
|
+
* (the later windowed-resolve work) need only reproduce the same edge SET, not the global insertion order —
|
|
26
|
+
* which removes "CSV row order == Map insertion order" as a byte-identical
|
|
27
|
+
* hazard for every later windowing step.
|
|
28
|
+
*/
|
|
29
|
+
const byGraphId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
30
|
+
const orderedNodes = (graph, sorted) => sorted ? [...graph.iterNodes()].sort(byGraphId) : graph.iterNodes();
|
|
31
|
+
const orderedRelationships = (graph, sorted) => sorted ? [...graph.iterRelationships()].sort(byGraphId) : graph.iterRelationships();
|
|
17
32
|
/** Flush buffered rows to disk every N rows */
|
|
18
33
|
const FLUSH_EVERY = 500;
|
|
19
34
|
// ============================================================================
|
|
@@ -185,6 +200,9 @@ class BufferedCSVWriter {
|
|
|
185
200
|
* File contents are lazy-read from disk with a generous LRU cache.
|
|
186
201
|
*/
|
|
187
202
|
export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
203
|
+
// Deterministic (id-sorted) node/relationship row order when enabled;
|
|
204
|
+
// default off = today's graph-insertion order (byte-identical).
|
|
205
|
+
const sortOutput = parseTruthyEnv(process.env.GITNEXUS_SORT_GRAPH_OUTPUT);
|
|
188
206
|
// Remove stale CSVs from previous crashed runs, then recreate
|
|
189
207
|
try {
|
|
190
208
|
await fs.rm(csvDir, { recursive: true, force: true });
|
|
@@ -253,7 +271,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
253
271
|
// A single Set covering every label prevents PK violations on COPY.
|
|
254
272
|
const seenNodeIds = new Set();
|
|
255
273
|
// --- SINGLE PASS over all nodes ---
|
|
256
|
-
for (const node of graph
|
|
274
|
+
for (const node of orderedNodes(graph, sortOutput)) {
|
|
257
275
|
if (seenNodeIds.has(node.id))
|
|
258
276
|
continue;
|
|
259
277
|
seenNodeIds.add(node.id);
|
|
@@ -421,7 +439,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
421
439
|
// --- Stream relationship CSV ---
|
|
422
440
|
const relCsvPath = path.join(csvDir, 'relations.csv');
|
|
423
441
|
const relWriter = new BufferedCSVWriter(relCsvPath, 'from,to,type,confidence,reason,step');
|
|
424
|
-
for (const rel of graph
|
|
442
|
+
for (const rel of orderedRelationships(graph, sortOutput)) {
|
|
425
443
|
await relWriter.addRow([
|
|
426
444
|
escapeCSVField(rel.sourceId),
|
|
427
445
|
escapeCSVField(rel.targetId),
|