gitnexus 1.6.4-rc.11 → 1.6.4-rc.110
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 +35 -0
- package/dist/_shared/index.d.ts +8 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +9 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/integrations/circuit-breaker.d.ts +183 -0
- package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -0
- package/dist/_shared/integrations/circuit-breaker.js +236 -0
- package/dist/_shared/integrations/circuit-breaker.js.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts +99 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.js +204 -0
- package/dist/_shared/integrations/resilient-fetch.js.map +1 -0
- package/dist/_shared/integrations/retry.d.ts +60 -0
- package/dist/_shared/integrations/retry.d.ts.map +1 -0
- package/dist/_shared/integrations/retry.js +67 -0
- package/dist/_shared/integrations/retry.js.map +1 -0
- package/dist/_shared/integrations/understand-quickly.d.ts +79 -0
- package/dist/_shared/integrations/understand-quickly.d.ts.map +1 -0
- package/dist/_shared/integrations/understand-quickly.js +139 -0
- package/dist/_shared/integrations/understand-quickly.js.map +1 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +11 -9
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +91 -36
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/test-helpers.d.ts +13 -0
- package/dist/_shared/test-helpers.d.ts.map +1 -0
- package/dist/_shared/test-helpers.js +13 -0
- package/dist/_shared/test-helpers.js.map +1 -0
- package/dist/cli/ai-context.js +2 -1
- package/dist/cli/analyze.d.ts +14 -1
- package/dist/cli/analyze.js +223 -40
- package/dist/cli/clean.js +4 -3
- package/dist/cli/cli-message.d.ts +15 -0
- package/dist/cli/cli-message.js +61 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +31 -0
- package/dist/cli/eval-server.js +37 -12
- package/dist/cli/group.js +4 -3
- package/dist/cli/index-repo.js +2 -2
- package/dist/cli/index.js +25 -2
- package/dist/cli/mcp.d.ts +21 -0
- package/dist/cli/mcp.js +52 -15
- package/dist/cli/optional-grammars.d.ts +47 -0
- package/dist/cli/optional-grammars.js +87 -0
- package/dist/cli/publish.d.ts +29 -0
- package/dist/cli/publish.js +174 -0
- package/dist/cli/remove.js +7 -4
- package/dist/cli/serve.js +31 -13
- package/dist/cli/setup.js +46 -16
- package/dist/cli/tool.js +6 -5
- package/dist/cli/wiki.js +39 -6
- package/dist/config/ignore-service.js +4 -1
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/embedding-mode.d.ts +21 -0
- package/dist/core/embedding-mode.js +18 -0
- package/dist/core/embeddings/config.d.ts +2 -0
- package/dist/core/embeddings/config.js +36 -0
- package/dist/core/embeddings/embedder.js +55 -27
- package/dist/core/embeddings/embedding-pipeline.d.ts +7 -1
- package/dist/core/embeddings/embedding-pipeline.js +106 -40
- package/dist/core/embeddings/exact-search.d.ts +15 -0
- package/dist/core/embeddings/exact-search.js +27 -0
- package/dist/core/embeddings/hf-env.d.ts +135 -0
- package/dist/core/embeddings/hf-env.js +232 -0
- package/dist/core/embeddings/http-client.d.ts +6 -0
- package/dist/core/embeddings/http-client.js +22 -18
- package/dist/core/embeddings/types.d.ts +4 -0
- package/dist/core/embeddings/types.js +2 -0
- package/dist/core/git-staleness.d.ts +6 -0
- package/dist/core/git-staleness.js +35 -5
- package/dist/core/group/bridge-db.js +319 -170
- package/dist/core/group/config-parser.js +23 -1
- package/dist/core/group/cross-impact.d.ts +44 -0
- package/dist/core/group/cross-impact.js +82 -8
- package/dist/core/group/extractors/elixir-workspace-extractor.d.ts +15 -0
- package/dist/core/group/extractors/elixir-workspace-extractor.js +204 -0
- package/dist/core/group/extractors/go-workspace-extractor.d.ts +14 -0
- package/dist/core/group/extractors/go-workspace-extractor.js +217 -0
- package/dist/core/group/extractors/grpc-extractor.js +15 -3
- package/dist/core/group/extractors/http-route-extractor.js +9 -1
- package/dist/core/group/extractors/include-extractor.d.ts +39 -0
- package/dist/core/group/extractors/include-extractor.js +565 -0
- package/dist/core/group/extractors/java-workspace-extractor.d.ts +16 -0
- package/dist/core/group/extractors/java-workspace-extractor.js +204 -0
- package/dist/core/group/extractors/manifest-extractor.js +34 -6
- package/dist/core/group/extractors/node-workspace-extractor.d.ts +14 -0
- package/dist/core/group/extractors/node-workspace-extractor.js +207 -0
- package/dist/core/group/extractors/python-workspace-extractor.d.ts +15 -0
- package/dist/core/group/extractors/python-workspace-extractor.js +205 -0
- package/dist/core/group/extractors/rust-workspace-extractor.d.ts +44 -0
- package/dist/core/group/extractors/rust-workspace-extractor.js +240 -0
- package/dist/core/group/extractors/thrift-extractor.d.ts +22 -0
- package/dist/core/group/extractors/thrift-extractor.js +282 -0
- package/dist/core/group/extractors/thrift-patterns/index.d.ts +4 -0
- package/dist/core/group/extractors/thrift-patterns/index.js +10 -0
- package/dist/core/group/extractors/thrift-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/thrift-patterns/java.js +220 -0
- package/dist/core/group/extractors/thrift-patterns/types.d.ts +17 -0
- package/dist/core/group/extractors/thrift-patterns/types.js +1 -0
- package/dist/core/group/extractors/topic-extractor.js +14 -14
- package/dist/core/group/extractors/workspace-extractor.d.ts +13 -0
- package/dist/core/group/extractors/workspace-extractor.js +65 -0
- package/dist/core/group/matching.d.ts +3 -3
- package/dist/core/group/matching.js +136 -50
- package/dist/core/group/service.d.ts +1 -0
- package/dist/core/group/service.js +5 -4
- package/dist/core/group/storage.js +70 -4
- package/dist/core/group/sync.js +85 -14
- package/dist/core/group/types.d.ts +22 -1
- package/dist/core/ingestion/ast-cache.js +2 -1
- package/dist/core/ingestion/call-processor.d.ts +3 -3
- package/dist/core/ingestion/call-processor.js +65 -69
- package/dist/core/ingestion/cluster-enricher.js +3 -2
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +4 -20
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +2 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +14 -2
- package/dist/core/ingestion/cpp-ue-preprocessor.d.ts +12 -0
- package/dist/core/ingestion/cpp-ue-preprocessor.js +260 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +0 -18
- package/dist/core/ingestion/entry-point-scoring.js +7 -188
- package/dist/core/ingestion/field-extractors/configs/csharp.js +8 -3
- package/dist/core/ingestion/filesystem-walker.js +3 -2
- package/dist/core/ingestion/framework-detection.d.ts +0 -120
- package/dist/core/ingestion/framework-detection.js +7 -365
- package/dist/core/ingestion/heritage-processor.js +12 -6
- package/dist/core/ingestion/import-processor.js +17 -14
- package/dist/core/ingestion/language-config.js +6 -5
- package/dist/core/ingestion/language-provider.d.ts +55 -0
- package/dist/core/ingestion/languages/c-cpp.js +72 -0
- package/dist/core/ingestion/languages/cobol.js +2 -0
- package/dist/core/ingestion/languages/csharp/captures.js +93 -0
- package/dist/core/ingestion/languages/csharp/query.js +6 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.js +36 -0
- package/dist/core/ingestion/languages/go/arity-metadata.d.ts +8 -0
- package/dist/core/ingestion/languages/go/arity-metadata.js +37 -0
- package/dist/core/ingestion/languages/go/arity.d.ts +2 -0
- package/dist/core/ingestion/languages/go/arity.js +14 -0
- package/dist/core/ingestion/languages/go/cache-stats.d.ts +7 -0
- package/dist/core/ingestion/languages/go/cache-stats.js +15 -0
- package/dist/core/ingestion/languages/go/captures.d.ts +2 -0
- package/dist/core/ingestion/languages/go/captures.js +128 -0
- package/dist/core/ingestion/languages/go/expand-wildcards.d.ts +15 -0
- package/dist/core/ingestion/languages/go/expand-wildcards.js +93 -0
- package/dist/core/ingestion/languages/go/import-decomposer.d.ts +3 -0
- package/dist/core/ingestion/languages/go/import-decomposer.js +44 -0
- package/dist/core/ingestion/languages/go/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/go/import-target.js +67 -0
- package/dist/core/ingestion/languages/go/index.d.ts +17 -0
- package/dist/core/ingestion/languages/go/index.js +17 -0
- package/dist/core/ingestion/languages/go/interface-impls.d.ts +4 -0
- package/dist/core/ingestion/languages/go/interface-impls.js +72 -0
- package/dist/core/ingestion/languages/go/interpret.d.ts +11 -0
- package/dist/core/ingestion/languages/go/interpret.js +146 -0
- package/dist/core/ingestion/languages/go/merge-bindings.d.ts +2 -0
- package/dist/core/ingestion/languages/go/merge-bindings.js +18 -0
- package/dist/core/ingestion/languages/go/method-owners.d.ts +17 -0
- package/dist/core/ingestion/languages/go/method-owners.js +96 -0
- package/dist/core/ingestion/languages/go/namespace-mirror.d.ts +15 -0
- package/dist/core/ingestion/languages/go/namespace-mirror.js +53 -0
- package/dist/core/ingestion/languages/go/package-siblings.d.ts +11 -0
- package/dist/core/ingestion/languages/go/package-siblings.js +84 -0
- package/dist/core/ingestion/languages/go/query.d.ts +3 -0
- package/dist/core/ingestion/languages/go/query.js +207 -0
- package/dist/core/ingestion/languages/go/range-binding.d.ts +8 -0
- package/dist/core/ingestion/languages/go/range-binding.js +108 -0
- package/dist/core/ingestion/languages/go/receiver-binding.d.ts +3 -0
- package/dist/core/ingestion/languages/go/receiver-binding.js +21 -0
- package/dist/core/ingestion/languages/go/scope-resolver.d.ts +2 -0
- package/dist/core/ingestion/languages/go/scope-resolver.js +33 -0
- package/dist/core/ingestion/languages/go/simple-hooks.d.ts +4 -0
- package/dist/core/ingestion/languages/go/simple-hooks.js +21 -0
- package/dist/core/ingestion/languages/go/type-binding.d.ts +3 -0
- package/dist/core/ingestion/languages/go/type-binding.js +237 -0
- package/dist/core/ingestion/languages/go.js +50 -0
- package/dist/core/ingestion/languages/java.js +21 -0
- package/dist/core/ingestion/languages/kotlin.js +41 -0
- package/dist/core/ingestion/languages/php.js +35 -0
- package/dist/core/ingestion/languages/python/captures.js +19 -4
- package/dist/core/ingestion/languages/python/import-target.js +121 -25
- package/dist/core/ingestion/languages/python.js +41 -0
- package/dist/core/ingestion/languages/ruby.js +25 -0
- package/dist/core/ingestion/languages/rust.js +35 -0
- package/dist/core/ingestion/languages/swift.js +68 -0
- package/dist/core/ingestion/languages/typescript/captures.js +22 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +7 -0
- package/dist/core/ingestion/languages/typescript/query.js +205 -5
- package/dist/core/ingestion/languages/typescript.js +132 -11
- package/dist/core/ingestion/languages/vue.js +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +3 -4
- package/dist/core/ingestion/method-extractors/generic.js +2 -1
- package/dist/core/ingestion/model/resolve.js +31 -11
- package/dist/core/ingestion/parsing-processor.js +27 -11
- package/dist/core/ingestion/pipeline-phases/cobol.js +4 -3
- package/dist/core/ingestion/pipeline-phases/communities.js +2 -1
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +5 -4
- package/dist/core/ingestion/pipeline-phases/cross-file.js +3 -2
- package/dist/core/ingestion/pipeline-phases/markdown.js +2 -1
- package/dist/core/ingestion/pipeline-phases/mro.js +2 -1
- package/dist/core/ingestion/pipeline-phases/orm.js +2 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +9 -8
- package/dist/core/ingestion/pipeline-phases/processes.js +12 -6
- package/dist/core/ingestion/pipeline-phases/routes.js +4 -3
- package/dist/core/ingestion/pipeline-phases/runner.js +3 -2
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +12 -5
- package/dist/core/ingestion/process-processor.js +4 -3
- package/dist/core/ingestion/registry-primary-flag.js +1 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +2 -2
- package/dist/core/ingestion/scope-extractor.js +18 -5
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +60 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +33 -3
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +3 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +49 -1
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +7 -1
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +1 -1
- package/dist/core/ingestion/scope-resolution/passes/mro.js +2 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +48 -25
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +3 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +27 -6
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +1 -1
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +7 -1
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +23 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
- package/dist/core/ingestion/tree-sitter-queries.js +149 -0
- package/dist/core/ingestion/type-env.js +2 -1
- package/dist/core/ingestion/type-extractors/swift.js +7 -4
- package/dist/core/ingestion/utils/ast-helpers.d.ts +12 -1
- package/dist/core/ingestion/utils/ast-helpers.js +15 -1
- package/dist/core/ingestion/utils/max-file-size.js +2 -1
- package/dist/core/ingestion/vue-sfc-extractor.js +18 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +21 -10
- package/dist/core/ingestion/workers/worker-pool.d.ts +11 -4
- package/dist/core/ingestion/workers/worker-pool.js +303 -49
- package/dist/core/lbug/csv-generator.js +5 -1
- package/dist/core/lbug/extension-loader.d.ts +86 -0
- package/dist/core/lbug/extension-loader.js +186 -0
- package/dist/core/lbug/lbug-adapter.d.ts +54 -17
- package/dist/core/lbug/lbug-adapter.js +202 -158
- package/dist/core/lbug/lbug-config.d.ts +102 -0
- package/dist/core/lbug/lbug-config.js +303 -0
- package/dist/core/lbug/pool-adapter.d.ts +1 -4
- package/dist/core/lbug/pool-adapter.js +82 -39
- package/dist/core/lbug/schema.d.ts +1 -1
- package/dist/core/lbug/schema.js +12 -1
- package/dist/core/logger.d.ts +125 -0
- package/dist/core/logger.js +323 -0
- package/dist/core/platform/capabilities.d.ts +24 -0
- package/dist/core/platform/capabilities.js +54 -0
- package/dist/core/run-analyze.d.ts +8 -1
- package/dist/core/run-analyze.js +71 -16
- package/dist/core/search/bm25-index.d.ts +6 -1
- package/dist/core/search/bm25-index.js +27 -10
- package/dist/core/search/hybrid-search.d.ts +4 -3
- package/dist/core/search/hybrid-search.js +6 -5
- package/dist/core/tree-sitter/parser-loader.d.ts +2 -2
- package/dist/core/tree-sitter/parser-loader.js +173 -68
- package/dist/core/wiki/cursor-client.js +2 -1
- package/dist/core/wiki/llm-client.d.ts +16 -0
- package/dist/core/wiki/llm-client.js +107 -74
- package/dist/mcp/compatible-stdio-transport.js +7 -1
- package/dist/mcp/core/embedder.js +37 -16
- package/dist/mcp/core/lbug-adapter.d.ts +7 -1
- package/dist/mcp/core/lbug-adapter.js +7 -1
- package/dist/mcp/local/local-backend.d.ts +9 -0
- package/dist/mcp/local/local-backend.js +199 -45
- package/dist/mcp/server.js +26 -8
- package/dist/mcp/stdio-capture.d.ts +40 -0
- package/dist/mcp/stdio-capture.js +53 -0
- package/dist/mcp/stdio-context.d.ts +47 -0
- package/dist/mcp/stdio-context.js +145 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +33 -0
- package/dist/server/api.d.ts +20 -0
- package/dist/server/api.js +222 -86
- package/dist/server/git-clone.d.ts +75 -2
- package/dist/server/git-clone.js +229 -13
- package/dist/server/mcp-http.js +2 -1
- package/dist/server/validation.d.ts +98 -0
- package/dist/server/validation.js +142 -0
- package/dist/storage/git.d.ts +57 -0
- package/dist/storage/git.js +124 -2
- package/dist/storage/repo-manager.d.ts +40 -2
- package/dist/storage/repo-manager.js +121 -13
- package/hooks/claude/gitnexus-hook.cjs +62 -3
- package/package.json +13 -10
- package/scripts/build-tree-sitter-dart.cjs +53 -0
- package/scripts/build-tree-sitter-proto.cjs +12 -1
- package/scripts/install-duckdb-extension.mjs +48 -0
- package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +16 -14
- package/vendor/tree-sitter-dart/README.md +18 -0
- package/vendor/tree-sitter-dart/binding.gyp +31 -0
- package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-dart/grammar.js +2895 -0
- package/vendor/tree-sitter-dart/package.json +18 -0
- package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
- package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
- package/vendor/tree-sitter-dart/queries/test.scm +1 -0
- package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
- package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
- package/vendor/tree-sitter-dart/src/parser.c +196127 -0
- package/vendor/tree-sitter-dart/src/scanner.c +130 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
- package/vendor/tree-sitter-swift/LICENSE +21 -0
- package/vendor/tree-sitter-swift/README.md +139 -0
- package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-swift/package.json +28 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
- package/web/assets/agent-BpHQleHE.js +597 -0
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BUzkWgDl.js +1 -0
- package/web/assets/{architectureDiagram-EMZXCZ2Q-Domyk_gO.js → architectureDiagram-EMZXCZ2Q-CXbzok1G.js} +3 -3
- package/web/assets/{blockDiagram-IGV67L2C-B_2kD7tM.js → blockDiagram-IGV67L2C-DA1QJ4ZB.js} +4 -4
- package/web/assets/{c4Diagram-DFAF54RM-BhJJW8Gg.js → c4Diagram-DFAF54RM-DeJV1rCn.js} +2 -2
- package/web/assets/{chunk-3GS5O3IE-jlWIjPsl.js → chunk-3GS5O3IE-B8hpnia8.js} +2 -2
- package/web/assets/{chunk-3YCYZ6SJ-Blq_IzZs.js → chunk-3YCYZ6SJ-B-BVu21C.js} +1 -1
- package/web/assets/{chunk-6NTNNK5N-DyPc58pp.js → chunk-6NTNNK5N-DW-jYSDr.js} +1 -1
- package/web/assets/{chunk-A34GCYZU-BI2i_LdU.js → chunk-A34GCYZU-BbZl-IKy.js} +1 -1
- package/web/assets/{chunk-DJ7UZH7F-i11ywiBl.js → chunk-DJ7UZH7F-B5tMfF87.js} +1 -1
- package/web/assets/{chunk-DKKBVRCY-1SffGI1N.js → chunk-DKKBVRCY-Djm-6K_e.js} +2 -2
- package/web/assets/{chunk-DU5LTGQ6-DaPeiwD5.js → chunk-DU5LTGQ6-QPoynYcp.js} +1 -1
- package/web/assets/{chunk-FXACKDTF-uhhi2PC2.js → chunk-FXACKDTF-jieSmbe2.js} +3 -3
- package/web/assets/chunk-H3VCZNTA-8EMHZ3SV.js +1 -0
- package/web/assets/chunk-HN6EAY2L-CgE_agsD.js +1 -0
- package/web/assets/{chunk-O5ABG6QK-Bt-Km84H.js → chunk-O5ABG6QK-DEZV1O19.js} +1 -1
- package/web/assets/{chunk-PK6DOVAG-ChlWY0BQ.js → chunk-PK6DOVAG-BXv0JnsJ.js} +3 -3
- package/web/assets/{chunk-RNJOYNJ4-B724K7cW.js → chunk-RNJOYNJ4-BiBkF9dN.js} +1 -1
- package/web/assets/chunk-RWUO3TPN-DTWzAhQ1.js +1 -0
- package/web/assets/chunk-TBF5ZNIQ-BSl6s9lT.js +1 -0
- package/web/assets/{chunk-TYMNRAUI-g1h33cq-.js → chunk-TYMNRAUI-D1NDLIMG.js} +1 -1
- package/web/assets/{chunk-W7ZLLLMY-Du-Hb9yb.js → chunk-W7ZLLLMY-Bu-cdLzs.js} +1 -1
- package/web/assets/{chunk-WSB5WSVC-B123clsZ.js → chunk-WSB5WSVC-Dor71SOz.js} +1 -1
- package/web/assets/{chunk-XGPFEOL4-BR7Eue38.js → chunk-XGPFEOL4-CUonLRle.js} +1 -1
- package/web/assets/classDiagram-PPOCWD7C-Cdd6UdVv.js +1 -0
- package/web/assets/classDiagram-v2-23LJLIIU-DNhxY2Ib.js +1 -0
- package/web/assets/{cose-bilkent-PNC4W37J-DCfErU-A.js → cose-bilkent-PNC4W37J-DdZq7Qz_.js} +1 -1
- package/web/assets/dagre-E77IOHMT-Cfj_6e17.js +4 -0
- package/web/assets/{diagram-H7BISOXX-CUVHlmAh.js → diagram-H7BISOXX-BNvpsrBv.js} +2 -2
- package/web/assets/{diagram-JC5VWROH-BoyOxulB.js → diagram-JC5VWROH-Cn0rtDbJ.js} +1 -1
- package/web/assets/{diagram-LXUTUG65-osr9hb7N.js → diagram-LXUTUG65-CHfoVKNm.js} +2 -2
- package/web/assets/{diagram-WEHSV5V5-d8nUqS39.js → diagram-WEHSV5V5-BLxHksnu.js} +1 -1
- package/web/assets/{erDiagram-GCSMX5X6-b-IwOhPS.js → erDiagram-GCSMX5X6-DfdN6iqF.js} +2 -2
- package/web/assets/flowDiagram-OTCZ4VVT-aAZw4c_M.js +162 -0
- package/web/assets/ganttDiagram-MUNLMDZQ-BEfC_7Or.js +292 -0
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-B6ZeRLP0.js +1 -0
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CG0kC8Q5.js +106 -0
- package/web/assets/{index-C_xK08EW.js → index-3NUCUW2s.js} +124 -123
- package/web/assets/info-OMHHGYJF-BF2H5H6G-BIOsZy0W.js +1 -0
- package/web/assets/infoDiagram-MN7RKWGX-DUoMH-pq.js +2 -0
- package/web/assets/{ishikawaDiagram-YMYX4NHK-CXsnC2FA.js → ishikawaDiagram-YMYX4NHK-BF8AtDaj.js} +2 -2
- package/web/assets/{journeyDiagram-SO5T7YLQ-BzZ07B-X.js → journeyDiagram-SO5T7YLQ-BW8oKa3N.js} +3 -3
- package/web/assets/{kanban-definition-LJHFXRCJ-C6_EpAd9.js → kanban-definition-LJHFXRCJ-DzbOsR-2.js} +6 -6
- package/web/assets/{mindmap-definition-2EUWGEK5-CCYGWZ1m.js → mindmap-definition-2EUWGEK5-BIXGQALq.js} +25 -25
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-Bs7ysNMU.js +1 -0
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-Csi8nnbH.js +1 -0
- package/web/assets/{pieDiagram-3IATQBI2-RyvRlQb4.js → pieDiagram-3IATQBI2-QY-u7P6a.js} +3 -3
- package/web/assets/{quadrantDiagram-E256RVCF-Bfb6sxCx.js → quadrantDiagram-E256RVCF-Di4aNiU8.js} +3 -3
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-DJlyXh2-.js +1 -0
- package/web/assets/{requirementDiagram-M5DCFWZL-DjvHDyvN.js → requirementDiagram-M5DCFWZL-CzUUrhQD.js} +4 -4
- package/web/assets/{sankeyDiagram-L3NBLAOT-CBCbbl8s.js → sankeyDiagram-L3NBLAOT-D2fbMiIU.js} +3 -3
- package/web/assets/{sequenceDiagram-ZOUHS735-BscU8TUR.js → sequenceDiagram-ZOUHS735-BNdkqguh.js} +6 -6
- package/web/assets/stateDiagram-MLPALWAM-51_Q2w2L.js +1 -0
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-8nHs2aNE.js +1 -0
- package/web/assets/{timeline-definition-5SPVSISX-DigPA1X8.js → timeline-definition-5SPVSISX-DkMiGj4Z.js} +9 -9
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-Cy6P0_CZ.js +1 -0
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-C_pp1R7A.js +1 -0
- package/web/assets/{vennDiagram-IE5QUKF5-C91UkZIf.js → vennDiagram-IE5QUKF5-BCxMmf9q.js} +3 -3
- package/web/assets/wardley-RL74JXVD-BCRCBASE-CMStJhpn.js +1 -0
- package/web/assets/{wardleyDiagram-XU3VSMPF-DloBhI0U.js → wardleyDiagram-XU3VSMPF-ijlLF77s.js} +4 -4
- package/web/assets/{xychartDiagram-ZHJ5623Y-BGWJvgwI.js → xychartDiagram-ZHJ5623Y-DZ7fP1_3.js} +3 -3
- package/web/index.html +1 -1
- package/scripts/patch-tree-sitter-swift.cjs +0 -78
- package/web/assets/agent-DaprsFSX.js +0 -597
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-DEdGaPg2.js +0 -1
- package/web/assets/chunk-H3VCZNTA-IchcISDt.js +0 -1
- package/web/assets/chunk-HN6EAY2L-D7ZFMNrB.js +0 -1
- package/web/assets/chunk-RWUO3TPN-DYn1XriD.js +0 -1
- package/web/assets/chunk-TBF5ZNIQ-DKtDz6ae.js +0 -1
- package/web/assets/classDiagram-PPOCWD7C-BglfKSs_.js +0 -1
- package/web/assets/classDiagram-v2-23LJLIIU-BSzTM28O.js +0 -1
- package/web/assets/dagre-E77IOHMT-tDRRhDoN.js +0 -4
- package/web/assets/flowDiagram-OTCZ4VVT-Ott2Q0AP.js +0 -162
- package/web/assets/ganttDiagram-MUNLMDZQ-BYtgN_5s.js +0 -292
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-CFyBIGZq.js +0 -1
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CsVD2gn4.js +0 -106
- package/web/assets/info-OMHHGYJF-BF2H5H6G-yjAxKEzh.js +0 -1
- package/web/assets/infoDiagram-MN7RKWGX-DXK0Unn5.js +0 -2
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-B8k4E3IT.js +0 -1
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-DdvfY118.js +0 -1
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-1EEDC_yU.js +0 -1
- package/web/assets/stateDiagram-MLPALWAM-CJusEK2D.js +0 -1
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-DImJ3PXD.js +0 -1
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-CzPDt3aG.js +0 -1
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-B9Iqiorr.js +0 -1
- package/web/assets/wardley-RL74JXVD-BCRCBASE-x42Qw7hp.js +0 -1
package/README.md
CHANGED
|
@@ -156,6 +156,7 @@ gitnexus analyze --embeddings # Enable embedding generation (slower, better s
|
|
|
156
156
|
gitnexus analyze --skip-agents-md # Preserve custom AGENTS.md/CLAUDE.md gitnexus section edits
|
|
157
157
|
gitnexus analyze --verbose # Log skipped files when parsers are unavailable
|
|
158
158
|
gitnexus analyze --max-file-size 1024 # Skip files larger than N KB (default: 512, cap: 32768)
|
|
159
|
+
gitnexus analyze --worker-timeout 60 # Increase worker idle timeout for slow parses
|
|
159
160
|
gitnexus mcp # Start MCP server (stdio) — serves all indexed repos
|
|
160
161
|
gitnexus serve # Start local HTTP server (multi-repo) for web UI
|
|
161
162
|
gitnexus index # Register an existing .gitnexus/ folder into the global registry
|
|
@@ -295,6 +296,25 @@ If `npm install -g gitnexus` fails on native modules:
|
|
|
295
296
|
npm install -g gitnexus
|
|
296
297
|
```
|
|
297
298
|
|
|
299
|
+
### Analyze warns about unavailable FTS or VECTOR extensions
|
|
300
|
+
|
|
301
|
+
GitNexus uses optional DuckDB extensions for BM25 and vector search. The `gitnexus serve` and MCP read paths only ever try to `LOAD` the extensions — they never block on a network install. The `analyze` command, by default, attempts one bounded out-of-process `INSTALL` if `LOAD` fails and proceeds even when that install times out, so the index is always written to disk; BM25/vector search degrade gracefully until the extensions become available.
|
|
302
|
+
|
|
303
|
+
Configure the behavior with two environment variables:
|
|
304
|
+
|
|
305
|
+
| Variable | Values | Default | Effect |
|
|
306
|
+
|----------|--------|---------|--------|
|
|
307
|
+
| `GITNEXUS_LBUG_EXTENSION_INSTALL` | `auto`, `load-only`, `never` | `auto` | `auto` runs one bounded INSTALL if LOAD fails. `load-only` only uses already-installed extensions (recommended for offline / firewalled environments). `never` skips optional extensions entirely. |
|
|
308
|
+
| `GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS` | positive integer | `15000` | Wall-clock budget for the out-of-process `INSTALL` child before it is killed. |
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# Offline/airgapped: never reach the network for extensions
|
|
312
|
+
GITNEXUS_LBUG_EXTENSION_INSTALL=load-only npx gitnexus analyze
|
|
313
|
+
|
|
314
|
+
# Slow network: give extension downloads more time
|
|
315
|
+
GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS=30000 npx gitnexus analyze
|
|
316
|
+
```
|
|
317
|
+
|
|
298
318
|
### Analysis runs out of memory
|
|
299
319
|
|
|
300
320
|
For very large repositories:
|
|
@@ -323,6 +343,21 @@ npx gitnexus analyze
|
|
|
323
343
|
|
|
324
344
|
Values above **32768 KB (32 MB)** are clamped to the tree-sitter parser ceiling; invalid values fall back to the 512 KB default with a one-time warning. When an override is active, `analyze` prints the effective threshold in its startup banner (e.g. `GITNEXUS_MAX_FILE_SIZE: effective threshold 2048KB (default 512KB)`).
|
|
325
345
|
|
|
346
|
+
### Analyze reports a worker timeout
|
|
347
|
+
|
|
348
|
+
Worker parse timeouts are recoverable. GitNexus retries stalled worker jobs with backoff, splits large jobs to isolate slow files, and falls back to the sequential parser when needed. If a large repository needs more time per worker job, use either:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# CLI flag, in seconds
|
|
352
|
+
npx gitnexus analyze --worker-timeout 60
|
|
353
|
+
|
|
354
|
+
# Environment variable, in milliseconds
|
|
355
|
+
export GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=60000
|
|
356
|
+
npx gitnexus analyze
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
For repositories with very large source files, `GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES` controls the worker job byte budget. The default is **8388608 bytes (8 MB)**.
|
|
360
|
+
|
|
326
361
|
## Privacy
|
|
327
362
|
|
|
328
363
|
- All processing happens locally on your machine
|
package/dist/_shared/index.d.ts
CHANGED
|
@@ -48,6 +48,14 @@ export { buildScopeTree, canParentScope, ScopeTreeInvariantError, } from './scop
|
|
|
48
48
|
export type { ScopeTree } from './scope-resolution/scope-tree.js';
|
|
49
49
|
export { buildPositionIndex } from './scope-resolution/position-index.js';
|
|
50
50
|
export type { PositionIndex } from './scope-resolution/position-index.js';
|
|
51
|
+
export { withRetry, computeBackoffMs } from './integrations/retry.js';
|
|
52
|
+
export type { RetryOptions, RetryDecision } from './integrations/retry.js';
|
|
53
|
+
export { CircuitBreaker, CircuitOpenError, getBreaker } from './integrations/circuit-breaker.js';
|
|
54
|
+
export type { CircuitBreakerOptions } from './integrations/circuit-breaker.js';
|
|
55
|
+
export { resilientFetch, ResilientFetchExhaustedError, RETRY_AFTER_CAP_MS, parseRetryAfter, } from './integrations/resilient-fetch.js';
|
|
56
|
+
export type { ResilientFetchOptions } from './integrations/resilient-fetch.js';
|
|
57
|
+
export { UNDERSTAND_QUICKLY_DISPATCH_URL, UNDERSTAND_QUICKLY_EVENT_TYPE, UNDERSTAND_QUICKLY_TOKEN_ENV, buildUqDispatchPayload, isValidOwnerRepo, parseOwnerRepoFromRemote, stripGitSuffix, } from './integrations/understand-quickly.js';
|
|
58
|
+
export type { UqDispatchPayload } from './integrations/understand-quickly.js';
|
|
51
59
|
export { diffResolutions } from './scope-resolution/shadow/diff.js';
|
|
52
60
|
export type { ShadowAgreement, ShadowCallsite, ShadowDiff, } from './scope-resolution/shadow/diff.js';
|
|
53
61
|
export { aggregateDiffs } from './scope-resolution/shadow/aggregate.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/_shared/index.js
CHANGED
|
@@ -33,6 +33,15 @@ export { CLASS_KINDS, METHOD_KINDS, FIELD_KINDS } from './scope-resolution/regis
|
|
|
33
33
|
export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
|
|
34
34
|
export { buildScopeTree, canParentScope, ScopeTreeInvariantError, } from './scope-resolution/scope-tree.js';
|
|
35
35
|
export { buildPositionIndex } from './scope-resolution/position-index.js';
|
|
36
|
+
// Resilient fetch primitives — bounded retries + per-process circuit breaker.
|
|
37
|
+
// Test-only helpers (`__resetBreakerRegistry__`, `classifyOutcome`) are
|
|
38
|
+
// reachable via the separate `gitnexus-shared/test-helpers` subpath; do
|
|
39
|
+
// NOT add them here. Production consumers must not call them.
|
|
40
|
+
export { withRetry, computeBackoffMs } from './integrations/retry.js';
|
|
41
|
+
export { CircuitBreaker, CircuitOpenError, getBreaker } from './integrations/circuit-breaker.js';
|
|
42
|
+
export { resilientFetch, ResilientFetchExhaustedError, RETRY_AFTER_CAP_MS, parseRetryAfter, } from './integrations/resilient-fetch.js';
|
|
43
|
+
// Understand-Quickly registry integration (opt-in)
|
|
44
|
+
export { UNDERSTAND_QUICKLY_DISPATCH_URL, UNDERSTAND_QUICKLY_EVENT_TYPE, UNDERSTAND_QUICKLY_TOKEN_ENV, buildUqDispatchPayload, isValidOwnerRepo, parseOwnerRepoFromRemote, stripGitSuffix, } from './integrations/understand-quickly.js';
|
|
36
45
|
// Shadow-mode diff + aggregation (RFC §6.3; Ring 2 SHARED #918)
|
|
37
46
|
export { diffResolutions } from './scope-resolution/shadow/diff.js';
|
|
38
47
|
export { aggregateDiffs } from './scope-resolution/shadow/aggregate.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAiCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAOxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAQlG,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAiCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAOxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAQlG,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,8EAA8E;AAC9E,wEAAwE;AACxE,wEAAwE;AACxE,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEjG,OAAO,EACL,cAAc,EACd,4BAA4B,EAC5B,kBAAkB,EAClB,eAAe,GAChB,MAAM,mCAAmC,CAAC;AAG3C,mDAAmD;AACnD,OAAO,EACL,+BAA+B,EAC/B,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,GACf,MAAM,sCAAsC,CAAC;AAG9C,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process circuit breaker.
|
|
3
|
+
*
|
|
4
|
+
* Closed -> Open transition fires after `failureThreshold` consecutive
|
|
5
|
+
* failures. While Open, `check` throws `CircuitOpenError` until
|
|
6
|
+
* `cooldownMs` has elapsed since the breaker tripped. The first call
|
|
7
|
+
* after the cooldown enters Half-Open and consumes the *probe permit*:
|
|
8
|
+
* a recorded success returns to Closed; a recorded failure flips back
|
|
9
|
+
* to Open with a fresh timestamp.
|
|
10
|
+
*
|
|
11
|
+
* Half-open admits exactly one in-flight probe at a time. Concurrent
|
|
12
|
+
* callers attempting `check()` while a probe is outstanding receive
|
|
13
|
+
* `CircuitOpenError` with `retryAfterMs = halfOpenRetryAfterMs` (default
|
|
14
|
+
* 1000ms; configurable). This prevents the recovery-time thundering
|
|
15
|
+
* herd that defeats the breaker's "fail fast" promise.
|
|
16
|
+
*
|
|
17
|
+
* Outcome reporting splits permit-release from state-resolution:
|
|
18
|
+
* - `recordSuccess` — releases the probe permit, resets the failure
|
|
19
|
+
* counter, transitions to Closed. Reserved for true 2xx/3xx outcomes.
|
|
20
|
+
* - `recordFailure` — releases the probe permit, increments the
|
|
21
|
+
* consecutive-failure counter, transitions to Open with a fresh
|
|
22
|
+
* `openedAt` (when called from Half-Open or when the threshold
|
|
23
|
+
* trips from Closed).
|
|
24
|
+
* - `recordNeutral` — releases the probe permit, BUT leaves state and
|
|
25
|
+
* counter untouched. Used for outcomes that are neither evidence of
|
|
26
|
+
* backend health nor evidence of backend failure (caller-driven
|
|
27
|
+
* cancellation, local timeout, terminal 4xx client errors). Critical
|
|
28
|
+
* design point: if `recordNeutral` did not release the permit, a
|
|
29
|
+
* single `TimeoutError` from per-attempt `AbortSignal.timeout` would
|
|
30
|
+
* route through `recordNeutral` and permanently park the breaker in
|
|
31
|
+
* half-open until process restart. Releasing the permit while leaving
|
|
32
|
+
* state half-open keeps the "neutral doesn't claim health" semantic
|
|
33
|
+
* without creating that wedge.
|
|
34
|
+
*
|
|
35
|
+
* Pairing invariant: every successful `check()` MUST be paired with
|
|
36
|
+
* exactly one `record*()` on every code path including throws. Direct
|
|
37
|
+
* consumers should wrap the protected operation in `try/finally`:
|
|
38
|
+
*
|
|
39
|
+
* breaker.check();
|
|
40
|
+
* try {
|
|
41
|
+
* const result = await operation();
|
|
42
|
+
* breaker.recordSuccess();
|
|
43
|
+
* return result;
|
|
44
|
+
* } catch (err) {
|
|
45
|
+
* // classify err and call recordFailure / recordNeutral / etc.
|
|
46
|
+
* throw err;
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* `resilientFetch`'s catch-all on `fetchImpl` already satisfies this
|
|
50
|
+
* for that consumer.
|
|
51
|
+
*
|
|
52
|
+
* Atomicity model: the half-open gate relies on JavaScript event-loop
|
|
53
|
+
* single-threadedness within a synchronous `check()` body. There is no
|
|
54
|
+
* `await` inside `check()`; concurrent callers serialize on microtask
|
|
55
|
+
* order, and exactly one observes `probeInFlight === false`. Do not
|
|
56
|
+
* introduce `await` inside `check()` without revisiting the gate. If
|
|
57
|
+
* this code is ever ported to a runtime with shared-memory threads
|
|
58
|
+
* (Node `worker_threads` with `SharedArrayBuffer`, Web Workers with
|
|
59
|
+
* shared registries), the boolean must become an atomic CAS — Resilience4j
|
|
60
|
+
* and Hystrix use atomic permits *because* they run in JVM thread pools.
|
|
61
|
+
*
|
|
62
|
+
* Runtime-agnostic: depends only on a `now()` clock and standard JS —
|
|
63
|
+
* no Node-only imports. Tests inject `now` to advance the clock
|
|
64
|
+
* deterministically without `vi.useFakeTimers()`.
|
|
65
|
+
*/
|
|
66
|
+
export declare class CircuitOpenError extends Error {
|
|
67
|
+
readonly name = "CircuitOpenError";
|
|
68
|
+
/** Approximate wait time before the breaker may transition to Half-Open
|
|
69
|
+
* (or before the in-flight probe is expected to resolve). */
|
|
70
|
+
readonly retryAfterMs: number;
|
|
71
|
+
constructor(retryAfterMs: number, key?: string);
|
|
72
|
+
}
|
|
73
|
+
export interface CircuitBreakerOptions {
|
|
74
|
+
/** Consecutive failures required to trip Closed -> Open. */
|
|
75
|
+
failureThreshold?: number;
|
|
76
|
+
/** Milliseconds Open before the next call may probe (Half-Open). */
|
|
77
|
+
cooldownMs?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Milliseconds to suggest in `CircuitOpenError.retryAfterMs` when the
|
|
80
|
+
* breaker is Half-Open with the probe permit consumed. Default 1000ms.
|
|
81
|
+
* Consumers with long-running protected ops (LLM streaming, large
|
|
82
|
+
* uploads) should raise this — the cooldown clock is no longer the
|
|
83
|
+
* right answer because cooldown has elapsed. Returning 0 invites
|
|
84
|
+
* retry storms; returning the full cooldown misleads about wait.
|
|
85
|
+
*/
|
|
86
|
+
halfOpenRetryAfterMs?: number;
|
|
87
|
+
/** Optional key for error messages and registry lookups. */
|
|
88
|
+
key?: string;
|
|
89
|
+
/** Clock override — defaults to `Date.now`. Tests inject deterministic time. */
|
|
90
|
+
now?: () => number;
|
|
91
|
+
}
|
|
92
|
+
type State = 'closed' | 'open' | 'half-open';
|
|
93
|
+
export declare class CircuitBreaker {
|
|
94
|
+
private readonly failureThreshold;
|
|
95
|
+
private readonly cooldownMs;
|
|
96
|
+
private readonly halfOpenRetryAfterMs;
|
|
97
|
+
private readonly key;
|
|
98
|
+
private readonly now;
|
|
99
|
+
private state;
|
|
100
|
+
private consecutiveFailures;
|
|
101
|
+
private openedAt;
|
|
102
|
+
/**
|
|
103
|
+
* True between a successful `check()` and the next `record*()` call
|
|
104
|
+
* during Half-Open. Gates concurrent callers from stampeding a still-
|
|
105
|
+
* recovering dependency. Boolean rather than counter — single-permit
|
|
106
|
+
* is the conservative end of the Hystrix/Resilience4j spectrum.
|
|
107
|
+
*/
|
|
108
|
+
private probeInFlight;
|
|
109
|
+
constructor(opts?: CircuitBreakerOptions);
|
|
110
|
+
/**
|
|
111
|
+
* Throw `CircuitOpenError` if the breaker won't admit this call.
|
|
112
|
+
* Otherwise consume the half-open probe permit (if applicable) and
|
|
113
|
+
* return so the caller can attempt the protected work.
|
|
114
|
+
*
|
|
115
|
+
* Three rejection paths:
|
|
116
|
+
* 1. Open and still in cooldown → throws with `retryAfterMs` =
|
|
117
|
+
* remaining cooldown.
|
|
118
|
+
* 2. Open with cooldown elapsed AND a probe is already in flight
|
|
119
|
+
* (race: another caller transitioned to half-open and grabbed
|
|
120
|
+
* the permit on a microtask before us) → throws with
|
|
121
|
+
* `halfOpenRetryAfterMs`.
|
|
122
|
+
* 3. Half-Open with probe in flight → throws with `halfOpenRetryAfterMs`.
|
|
123
|
+
*
|
|
124
|
+
* **Pairing invariant**: every successful return from `check()` MUST
|
|
125
|
+
* be paired with exactly one `recordSuccess` / `recordFailure` /
|
|
126
|
+
* `recordNeutral` on every code path including thrown exceptions.
|
|
127
|
+
* Failing to pair leaves the probe permit consumed forever and
|
|
128
|
+
* wedges the breaker. See file-header JSDoc for the canonical
|
|
129
|
+
* try/finally pattern.
|
|
130
|
+
*/
|
|
131
|
+
check(): void;
|
|
132
|
+
recordSuccess(): void;
|
|
133
|
+
recordFailure(): void;
|
|
134
|
+
/**
|
|
135
|
+
* Releases the probe permit BUT leaves state and counter untouched.
|
|
136
|
+
* Use when an attempt produced a response or error that should not
|
|
137
|
+
* influence breaker health in either direction — caller-driven aborts,
|
|
138
|
+
* local AbortSignal timeouts, terminal 4xx client errors.
|
|
139
|
+
*
|
|
140
|
+
* Why permit-release-without-state-resolution: if `recordNeutral` did
|
|
141
|
+
* not clear `probeInFlight`, a single `TimeoutError` from per-attempt
|
|
142
|
+
* `AbortSignal.timeout` (which routes through neutral classification)
|
|
143
|
+
* would permanently park the breaker in half-open. Since timeouts are
|
|
144
|
+
* an *expected* outcome under flaky-dependency conditions, the cited
|
|
145
|
+
* "per-attempt timeout bounds the stuck state" mitigation would itself
|
|
146
|
+
* be the trigger for a permanent wedge. Releasing the permit closes
|
|
147
|
+
* that loop while keeping the "neutral doesn't claim dependency
|
|
148
|
+
* health" semantic.
|
|
149
|
+
*
|
|
150
|
+
* Calling `recordSuccess` for these would erase legitimate prior
|
|
151
|
+
* failure signal; calling `recordFailure` would trip the breaker for
|
|
152
|
+
* outcomes the backend isn't responsible for.
|
|
153
|
+
*/
|
|
154
|
+
recordNeutral(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Pure read — no state mutation, no permit accounting. Returns the
|
|
157
|
+
* *would-be* state at the current instant: 'half-open' if the breaker
|
|
158
|
+
* is open with cooldown elapsed (regardless of whether a probe is in
|
|
159
|
+
* flight), 'open' if open and still in cooldown, 'closed' otherwise.
|
|
160
|
+
*
|
|
161
|
+
* Inspection-only; safe to call from tests without consuming a probe
|
|
162
|
+
* permit. The implicit Open -> Half-Open transition that mutates
|
|
163
|
+
* `state` lives in `check()` only.
|
|
164
|
+
*/
|
|
165
|
+
getState(): State;
|
|
166
|
+
getConsecutiveFailures(): number;
|
|
167
|
+
/** Inspection-only test accessor for the half-open probe permit. */
|
|
168
|
+
isProbeInFlight(): boolean;
|
|
169
|
+
/** Timestamp (ms since epoch) when the breaker last transitioned to Open,
|
|
170
|
+
* or `null` if it's currently Closed. Useful for computing remaining
|
|
171
|
+
* cooldown without consuming a probe permit via `check()`. */
|
|
172
|
+
getOpenedAt(): number | null;
|
|
173
|
+
/** Configured cooldown duration in milliseconds. */
|
|
174
|
+
getCooldownMs(): number;
|
|
175
|
+
}
|
|
176
|
+
export declare function getBreaker(key: string, opts?: CircuitBreakerOptions): CircuitBreaker;
|
|
177
|
+
/**
|
|
178
|
+
* Test-only: clear all registered breakers. Tests must call this in
|
|
179
|
+
* `beforeEach` to prevent breaker state from leaking across test cases.
|
|
180
|
+
*/
|
|
181
|
+
export declare function __resetBreakerRegistry__(): void;
|
|
182
|
+
export {};
|
|
183
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/integrations/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAEH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAkB,IAAI,sBAAsB;IAC5C;kEAC8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,YAAY,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;CAQ/C;AAED,MAAM,WAAW,qBAAqB;IACpC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,KAAK,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE7C,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAuB;IACvC;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAS;gBAElB,IAAI,GAAE,qBAA0B;IAQ5C;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,IAAI,IAAI;IAqBb,aAAa,IAAI,IAAI;IAOrB,aAAa,IAAI,IAAI;IASrB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,aAAa,IAAI,IAAI;IAKrB;;;;;;;;;OASG;IACH,QAAQ,IAAI,KAAK;IAOjB,sBAAsB,IAAI,MAAM;IAGhC,oEAAoE;IACpE,eAAe,IAAI,OAAO;IAG1B;;mEAE+D;IAC/D,WAAW,IAAI,MAAM,GAAG,IAAI;IAG5B,oDAAoD;IACpD,aAAa,IAAI,MAAM;CAGxB;AAUD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAOpF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process circuit breaker.
|
|
3
|
+
*
|
|
4
|
+
* Closed -> Open transition fires after `failureThreshold` consecutive
|
|
5
|
+
* failures. While Open, `check` throws `CircuitOpenError` until
|
|
6
|
+
* `cooldownMs` has elapsed since the breaker tripped. The first call
|
|
7
|
+
* after the cooldown enters Half-Open and consumes the *probe permit*:
|
|
8
|
+
* a recorded success returns to Closed; a recorded failure flips back
|
|
9
|
+
* to Open with a fresh timestamp.
|
|
10
|
+
*
|
|
11
|
+
* Half-open admits exactly one in-flight probe at a time. Concurrent
|
|
12
|
+
* callers attempting `check()` while a probe is outstanding receive
|
|
13
|
+
* `CircuitOpenError` with `retryAfterMs = halfOpenRetryAfterMs` (default
|
|
14
|
+
* 1000ms; configurable). This prevents the recovery-time thundering
|
|
15
|
+
* herd that defeats the breaker's "fail fast" promise.
|
|
16
|
+
*
|
|
17
|
+
* Outcome reporting splits permit-release from state-resolution:
|
|
18
|
+
* - `recordSuccess` — releases the probe permit, resets the failure
|
|
19
|
+
* counter, transitions to Closed. Reserved for true 2xx/3xx outcomes.
|
|
20
|
+
* - `recordFailure` — releases the probe permit, increments the
|
|
21
|
+
* consecutive-failure counter, transitions to Open with a fresh
|
|
22
|
+
* `openedAt` (when called from Half-Open or when the threshold
|
|
23
|
+
* trips from Closed).
|
|
24
|
+
* - `recordNeutral` — releases the probe permit, BUT leaves state and
|
|
25
|
+
* counter untouched. Used for outcomes that are neither evidence of
|
|
26
|
+
* backend health nor evidence of backend failure (caller-driven
|
|
27
|
+
* cancellation, local timeout, terminal 4xx client errors). Critical
|
|
28
|
+
* design point: if `recordNeutral` did not release the permit, a
|
|
29
|
+
* single `TimeoutError` from per-attempt `AbortSignal.timeout` would
|
|
30
|
+
* route through `recordNeutral` and permanently park the breaker in
|
|
31
|
+
* half-open until process restart. Releasing the permit while leaving
|
|
32
|
+
* state half-open keeps the "neutral doesn't claim health" semantic
|
|
33
|
+
* without creating that wedge.
|
|
34
|
+
*
|
|
35
|
+
* Pairing invariant: every successful `check()` MUST be paired with
|
|
36
|
+
* exactly one `record*()` on every code path including throws. Direct
|
|
37
|
+
* consumers should wrap the protected operation in `try/finally`:
|
|
38
|
+
*
|
|
39
|
+
* breaker.check();
|
|
40
|
+
* try {
|
|
41
|
+
* const result = await operation();
|
|
42
|
+
* breaker.recordSuccess();
|
|
43
|
+
* return result;
|
|
44
|
+
* } catch (err) {
|
|
45
|
+
* // classify err and call recordFailure / recordNeutral / etc.
|
|
46
|
+
* throw err;
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* `resilientFetch`'s catch-all on `fetchImpl` already satisfies this
|
|
50
|
+
* for that consumer.
|
|
51
|
+
*
|
|
52
|
+
* Atomicity model: the half-open gate relies on JavaScript event-loop
|
|
53
|
+
* single-threadedness within a synchronous `check()` body. There is no
|
|
54
|
+
* `await` inside `check()`; concurrent callers serialize on microtask
|
|
55
|
+
* order, and exactly one observes `probeInFlight === false`. Do not
|
|
56
|
+
* introduce `await` inside `check()` without revisiting the gate. If
|
|
57
|
+
* this code is ever ported to a runtime with shared-memory threads
|
|
58
|
+
* (Node `worker_threads` with `SharedArrayBuffer`, Web Workers with
|
|
59
|
+
* shared registries), the boolean must become an atomic CAS — Resilience4j
|
|
60
|
+
* and Hystrix use atomic permits *because* they run in JVM thread pools.
|
|
61
|
+
*
|
|
62
|
+
* Runtime-agnostic: depends only on a `now()` clock and standard JS —
|
|
63
|
+
* no Node-only imports. Tests inject `now` to advance the clock
|
|
64
|
+
* deterministically without `vi.useFakeTimers()`.
|
|
65
|
+
*/
|
|
66
|
+
export class CircuitOpenError extends Error {
|
|
67
|
+
name = 'CircuitOpenError';
|
|
68
|
+
/** Approximate wait time before the breaker may transition to Half-Open
|
|
69
|
+
* (or before the in-flight probe is expected to resolve). */
|
|
70
|
+
retryAfterMs;
|
|
71
|
+
constructor(retryAfterMs, key) {
|
|
72
|
+
super(key
|
|
73
|
+
? `Circuit '${key}' is open; retry in ${Math.ceil(retryAfterMs / 1000)}s`
|
|
74
|
+
: `Circuit is open; retry in ${Math.ceil(retryAfterMs / 1000)}s`);
|
|
75
|
+
this.retryAfterMs = retryAfterMs;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class CircuitBreaker {
|
|
79
|
+
failureThreshold;
|
|
80
|
+
cooldownMs;
|
|
81
|
+
halfOpenRetryAfterMs;
|
|
82
|
+
key;
|
|
83
|
+
now;
|
|
84
|
+
state = 'closed';
|
|
85
|
+
consecutiveFailures = 0;
|
|
86
|
+
openedAt = null;
|
|
87
|
+
/**
|
|
88
|
+
* True between a successful `check()` and the next `record*()` call
|
|
89
|
+
* during Half-Open. Gates concurrent callers from stampeding a still-
|
|
90
|
+
* recovering dependency. Boolean rather than counter — single-permit
|
|
91
|
+
* is the conservative end of the Hystrix/Resilience4j spectrum.
|
|
92
|
+
*/
|
|
93
|
+
probeInFlight = false;
|
|
94
|
+
constructor(opts = {}) {
|
|
95
|
+
this.failureThreshold = opts.failureThreshold ?? 3;
|
|
96
|
+
this.cooldownMs = opts.cooldownMs ?? 30_000;
|
|
97
|
+
this.halfOpenRetryAfterMs = opts.halfOpenRetryAfterMs ?? 1_000;
|
|
98
|
+
this.key = opts.key;
|
|
99
|
+
this.now = opts.now ?? (() => Date.now());
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Throw `CircuitOpenError` if the breaker won't admit this call.
|
|
103
|
+
* Otherwise consume the half-open probe permit (if applicable) and
|
|
104
|
+
* return so the caller can attempt the protected work.
|
|
105
|
+
*
|
|
106
|
+
* Three rejection paths:
|
|
107
|
+
* 1. Open and still in cooldown → throws with `retryAfterMs` =
|
|
108
|
+
* remaining cooldown.
|
|
109
|
+
* 2. Open with cooldown elapsed AND a probe is already in flight
|
|
110
|
+
* (race: another caller transitioned to half-open and grabbed
|
|
111
|
+
* the permit on a microtask before us) → throws with
|
|
112
|
+
* `halfOpenRetryAfterMs`.
|
|
113
|
+
* 3. Half-Open with probe in flight → throws with `halfOpenRetryAfterMs`.
|
|
114
|
+
*
|
|
115
|
+
* **Pairing invariant**: every successful return from `check()` MUST
|
|
116
|
+
* be paired with exactly one `recordSuccess` / `recordFailure` /
|
|
117
|
+
* `recordNeutral` on every code path including thrown exceptions.
|
|
118
|
+
* Failing to pair leaves the probe permit consumed forever and
|
|
119
|
+
* wedges the breaker. See file-header JSDoc for the canonical
|
|
120
|
+
* try/finally pattern.
|
|
121
|
+
*/
|
|
122
|
+
check() {
|
|
123
|
+
if (this.state === 'open' && this.openedAt !== null) {
|
|
124
|
+
const elapsed = this.now() - this.openedAt;
|
|
125
|
+
if (elapsed < this.cooldownMs) {
|
|
126
|
+
throw new CircuitOpenError(this.cooldownMs - elapsed, this.key);
|
|
127
|
+
}
|
|
128
|
+
// Cooldown elapsed — transition to Half-Open. The very next
|
|
129
|
+
// `probeInFlight` check below decides whether THIS caller gets
|
|
130
|
+
// the permit or hits the gate.
|
|
131
|
+
this.state = 'half-open';
|
|
132
|
+
}
|
|
133
|
+
if (this.state === 'half-open') {
|
|
134
|
+
if (this.probeInFlight) {
|
|
135
|
+
throw new CircuitOpenError(this.halfOpenRetryAfterMs, this.key);
|
|
136
|
+
}
|
|
137
|
+
this.probeInFlight = true;
|
|
138
|
+
}
|
|
139
|
+
// Closed state falls through silently.
|
|
140
|
+
}
|
|
141
|
+
recordSuccess() {
|
|
142
|
+
this.probeInFlight = false;
|
|
143
|
+
this.consecutiveFailures = 0;
|
|
144
|
+
this.state = 'closed';
|
|
145
|
+
this.openedAt = null;
|
|
146
|
+
}
|
|
147
|
+
recordFailure() {
|
|
148
|
+
this.probeInFlight = false;
|
|
149
|
+
this.consecutiveFailures += 1;
|
|
150
|
+
if (this.state === 'half-open' || this.consecutiveFailures >= this.failureThreshold) {
|
|
151
|
+
this.state = 'open';
|
|
152
|
+
this.openedAt = this.now();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Releases the probe permit BUT leaves state and counter untouched.
|
|
157
|
+
* Use when an attempt produced a response or error that should not
|
|
158
|
+
* influence breaker health in either direction — caller-driven aborts,
|
|
159
|
+
* local AbortSignal timeouts, terminal 4xx client errors.
|
|
160
|
+
*
|
|
161
|
+
* Why permit-release-without-state-resolution: if `recordNeutral` did
|
|
162
|
+
* not clear `probeInFlight`, a single `TimeoutError` from per-attempt
|
|
163
|
+
* `AbortSignal.timeout` (which routes through neutral classification)
|
|
164
|
+
* would permanently park the breaker in half-open. Since timeouts are
|
|
165
|
+
* an *expected* outcome under flaky-dependency conditions, the cited
|
|
166
|
+
* "per-attempt timeout bounds the stuck state" mitigation would itself
|
|
167
|
+
* be the trigger for a permanent wedge. Releasing the permit closes
|
|
168
|
+
* that loop while keeping the "neutral doesn't claim dependency
|
|
169
|
+
* health" semantic.
|
|
170
|
+
*
|
|
171
|
+
* Calling `recordSuccess` for these would erase legitimate prior
|
|
172
|
+
* failure signal; calling `recordFailure` would trip the breaker for
|
|
173
|
+
* outcomes the backend isn't responsible for.
|
|
174
|
+
*/
|
|
175
|
+
recordNeutral() {
|
|
176
|
+
this.probeInFlight = false;
|
|
177
|
+
// State and consecutiveFailures are preserved by design.
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Pure read — no state mutation, no permit accounting. Returns the
|
|
181
|
+
* *would-be* state at the current instant: 'half-open' if the breaker
|
|
182
|
+
* is open with cooldown elapsed (regardless of whether a probe is in
|
|
183
|
+
* flight), 'open' if open and still in cooldown, 'closed' otherwise.
|
|
184
|
+
*
|
|
185
|
+
* Inspection-only; safe to call from tests without consuming a probe
|
|
186
|
+
* permit. The implicit Open -> Half-Open transition that mutates
|
|
187
|
+
* `state` lives in `check()` only.
|
|
188
|
+
*/
|
|
189
|
+
getState() {
|
|
190
|
+
if (this.state === 'open' && this.openedAt !== null) {
|
|
191
|
+
const elapsed = this.now() - this.openedAt;
|
|
192
|
+
if (elapsed >= this.cooldownMs)
|
|
193
|
+
return 'half-open';
|
|
194
|
+
}
|
|
195
|
+
return this.state;
|
|
196
|
+
}
|
|
197
|
+
getConsecutiveFailures() {
|
|
198
|
+
return this.consecutiveFailures;
|
|
199
|
+
}
|
|
200
|
+
/** Inspection-only test accessor for the half-open probe permit. */
|
|
201
|
+
isProbeInFlight() {
|
|
202
|
+
return this.probeInFlight;
|
|
203
|
+
}
|
|
204
|
+
/** Timestamp (ms since epoch) when the breaker last transitioned to Open,
|
|
205
|
+
* or `null` if it's currently Closed. Useful for computing remaining
|
|
206
|
+
* cooldown without consuming a probe permit via `check()`. */
|
|
207
|
+
getOpenedAt() {
|
|
208
|
+
return this.openedAt;
|
|
209
|
+
}
|
|
210
|
+
/** Configured cooldown duration in milliseconds. */
|
|
211
|
+
getCooldownMs() {
|
|
212
|
+
return this.cooldownMs;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ─── Per-process registry ────────────────────────────────────────────
|
|
216
|
+
//
|
|
217
|
+
// Single shared map keyed on caller-chosen strings. Used by
|
|
218
|
+
// `resilient-fetch.ts` so multiple call sites targeting the same logical
|
|
219
|
+
// endpoint share breaker state. Per-process only — not persisted.
|
|
220
|
+
const registry = new Map();
|
|
221
|
+
export function getBreaker(key, opts) {
|
|
222
|
+
let breaker = registry.get(key);
|
|
223
|
+
if (!breaker) {
|
|
224
|
+
breaker = new CircuitBreaker({ ...opts, key });
|
|
225
|
+
registry.set(key, breaker);
|
|
226
|
+
}
|
|
227
|
+
return breaker;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Test-only: clear all registered breakers. Tests must call this in
|
|
231
|
+
* `beforeEach` to prevent breaker state from leaking across test cases.
|
|
232
|
+
*/
|
|
233
|
+
export function __resetBreakerRegistry__() {
|
|
234
|
+
registry.clear();
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/integrations/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `resilientFetch` — fetch wrapped in retry + circuit breaker, with
|
|
3
|
+
* GitHub-flavoured retry classification baked in (Retry-After parsing,
|
|
4
|
+
* 401/403/404/422 treated as terminal client errors).
|
|
5
|
+
*
|
|
6
|
+
* Designed for the `gitnexus publish` GitHub `repository_dispatch`
|
|
7
|
+
* call, but the classification rules apply to any GitHub REST endpoint.
|
|
8
|
+
* Runtime-agnostic — no Node-only imports.
|
|
9
|
+
*/
|
|
10
|
+
import { CircuitBreaker, CircuitOpenError, type CircuitBreakerOptions } from './circuit-breaker.js';
|
|
11
|
+
import { type RetryOptions } from './retry.js';
|
|
12
|
+
export { CircuitOpenError };
|
|
13
|
+
export interface ResilientFetchOptions {
|
|
14
|
+
/** Optional fetch implementation override. Defaults to `globalThis.fetch`. */
|
|
15
|
+
fetchImpl?: typeof fetch;
|
|
16
|
+
/**
|
|
17
|
+
* Logical key for the breaker. Defaults to `<host><pathname>` of the
|
|
18
|
+
* request URL — call sites targeting the same endpoint share breaker
|
|
19
|
+
* state regardless of query-string differences.
|
|
20
|
+
*/
|
|
21
|
+
breakerKey?: string;
|
|
22
|
+
/** Per-call breaker override. Used for tests and one-off configuration. */
|
|
23
|
+
breaker?: CircuitBreaker;
|
|
24
|
+
/** Tuning knobs for the breaker registered under `breakerKey`. */
|
|
25
|
+
breakerOptions?: CircuitBreakerOptions;
|
|
26
|
+
/** Tuning knobs for the retry helper. */
|
|
27
|
+
retry?: Partial<Pick<RetryOptions, 'maxAttempts' | 'baseDelayMs' | 'capDelayMs'>> & {
|
|
28
|
+
sleep?: RetryOptions['sleep'];
|
|
29
|
+
random?: RetryOptions['random'];
|
|
30
|
+
};
|
|
31
|
+
/** Clock override propagated into Retry-After HTTP-date math and breaker. */
|
|
32
|
+
now?: () => number;
|
|
33
|
+
}
|
|
34
|
+
/** Cap on any single Retry-After wait — protects CLI from a buggy registry. */
|
|
35
|
+
export declare const RETRY_AFTER_CAP_MS = 30000;
|
|
36
|
+
/**
|
|
37
|
+
* Parse a `Retry-After` header value into milliseconds.
|
|
38
|
+
* Accepts either a delta-seconds integer (`"30"`) or an HTTP-date.
|
|
39
|
+
* Returns null on parse failure or negative deltas.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseRetryAfter(value: string | null, now?: () => number): number | null;
|
|
42
|
+
/** Internal: outcome classification used by the resilientFetch loop. */
|
|
43
|
+
type Outcome = {
|
|
44
|
+
kind: 'success';
|
|
45
|
+
resp: Response;
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'terminal-client';
|
|
48
|
+
resp: Response;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'retryable-status';
|
|
51
|
+
resp: Response;
|
|
52
|
+
afterMs: number | undefined;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'terminal-network';
|
|
55
|
+
err: unknown;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'retryable-network';
|
|
58
|
+
err: unknown;
|
|
59
|
+
};
|
|
60
|
+
/** Exported for unit tests. */
|
|
61
|
+
export declare function classifyOutcome(result: {
|
|
62
|
+
kind: 'error';
|
|
63
|
+
err: unknown;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'response';
|
|
66
|
+
resp: Response;
|
|
67
|
+
}, now: () => number): Outcome;
|
|
68
|
+
/** Final error thrown when retries are exhausted on a 5xx / 429. */
|
|
69
|
+
export declare class ResilientFetchExhaustedError extends Error {
|
|
70
|
+
readonly response: Response;
|
|
71
|
+
readonly name = "ResilientFetchExhaustedError";
|
|
72
|
+
constructor(response: Response);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Wrap `fetch` with bounded retries and a per-process circuit breaker.
|
|
76
|
+
*
|
|
77
|
+
* Semantics:
|
|
78
|
+
* - 5xx and 429 responses are retried; 429 honors `Retry-After` (capped).
|
|
79
|
+
* - Network throws are retried unless they are `TimeoutError` DOMExceptions.
|
|
80
|
+
* - Timeouts and 4xx (other than 429) are returned/thrown without retry
|
|
81
|
+
* AND without incrementing the breaker — they reflect caller config
|
|
82
|
+
* or local network state, not registry health.
|
|
83
|
+
* - Each `fetch` call carries the caller-supplied `signal` (e.g. an
|
|
84
|
+
* `AbortSignal.timeout()`) — that timeout bounds each individual
|
|
85
|
+
* attempt, not the whole retry sequence.
|
|
86
|
+
* - When the breaker is open, throws `CircuitOpenError` synchronously
|
|
87
|
+
* without invoking `fetch`.
|
|
88
|
+
* - When retries are exhausted on a 5xx / 429, throws
|
|
89
|
+
* `ResilientFetchExhaustedError` carrying the last response.
|
|
90
|
+
*
|
|
91
|
+
* Cumulative wall-clock budget:
|
|
92
|
+
* maxAttempts × (per-attempt-timeout + capDelayMs)
|
|
93
|
+
* With defaults (3, 500ms base, 5000ms cap) and a typical 15s per-attempt
|
|
94
|
+
* timeout from the caller's signal, worst case is ~3 × (15s + 5s) = 60s.
|
|
95
|
+
* Callers that want a tighter total bound should reduce `maxAttempts` or
|
|
96
|
+
* wrap `resilientFetch` in their own outer `AbortSignal.timeout()`.
|
|
97
|
+
*/
|
|
98
|
+
export declare function resilientFetch(input: string | URL, init: RequestInit | undefined, opts?: ResilientFetchOptions): Promise<Response>;
|
|
99
|
+
//# sourceMappingURL=resilient-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilient-fetch.d.ts","sourceRoot":"","sources":["../../src/integrations/resilient-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,gBAAgB,EAEhB,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,kEAAkE;IAClE,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC,GAAG;QAClF,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;KACjC,CAAC;IACF,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAQzC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,MAAM,GAAG,IAAI,CAejG;AAED,wEAAwE;AACxE,KAAK,OAAO,GACR;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhD,+BAA+B;AAC/B,wBAAgB,eAAe,CAC7B,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,EAC9E,GAAG,EAAE,MAAM,MAAM,GAChB,OAAO,CAiCT;AAcD,oEAAoE;AACpE,qBAAa,4BAA6B,SAAQ,KAAK;aAEzB,QAAQ,EAAE,QAAQ;IAD9C,SAAkB,IAAI,kCAAkC;gBAC5B,QAAQ,EAAE,QAAQ;CAG/C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,QAAQ,CAAC,CA8GnB"}
|