gitnexus 1.6.11-rc.8 → 1.6.11
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 +95 -26
- package/dist/_shared/graph/types.d.ts +81 -2
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -1
- package/dist/_shared/integrations/circuit-breaker.js.map +1 -1
- package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
- package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +2 -1
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +2 -2
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +4 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +1 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.d.ts +3 -0
- package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.js +4 -0
- package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +10 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +15 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +4 -0
- package/dist/cli/ai-context.js +90 -14
- package/dist/cli/analyze-config.js +14 -0
- package/dist/cli/analyze-options.d.ts +14 -0
- package/dist/cli/analyze-watch.d.ts +20 -0
- package/dist/cli/analyze-watch.js +434 -0
- package/dist/cli/analyze.js +27 -9
- package/dist/cli/auto-sync.d.ts +1 -0
- package/dist/cli/auto-sync.js +107 -0
- package/dist/cli/command-banner.d.ts +15 -0
- package/dist/cli/command-banner.js +42 -0
- package/dist/cli/detect-changes-format.js +21 -0
- package/dist/cli/doctor.d.ts +7 -0
- package/dist/cli/doctor.js +14 -0
- package/dist/cli/group.js +7 -1
- package/dist/cli/help-i18n.js +4 -0
- package/dist/cli/i18n/en.d.ts +18 -3
- package/dist/cli/i18n/en.js +18 -3
- package/dist/cli/i18n/resources.d.ts +33 -3
- package/dist/cli/i18n/zh-CN.d.ts +15 -0
- package/dist/cli/i18n/zh-CN.js +18 -3
- package/dist/cli/index.js +34 -7
- package/dist/cli/mcp.d.ts +15 -0
- package/dist/cli/mcp.js +44 -0
- package/dist/cli/optional-grammars.d.ts +3 -2
- package/dist/cli/optional-grammars.js +19 -3
- package/dist/cli/setup.js +41 -15
- package/dist/cli/update-notice.d.ts +34 -0
- package/dist/cli/update-notice.js +91 -0
- package/dist/cli/update.d.ts +21 -0
- package/dist/cli/update.js +93 -0
- package/dist/cli/watch.d.ts +1 -20
- package/dist/cli/watch.js +5 -371
- package/dist/core/analysis-feature-registry.d.ts +2 -0
- package/dist/core/analysis-feature-registry.js +15 -0
- package/dist/core/analyzer-identity.js +21 -3
- package/dist/core/auto-sync/analysis-worker-launch.d.ts +30 -0
- package/dist/core/auto-sync/analysis-worker-launch.js +166 -0
- package/dist/core/auto-sync/config.d.ts +33 -0
- package/dist/core/auto-sync/config.js +320 -0
- package/dist/core/auto-sync/index.d.ts +6 -0
- package/dist/core/auto-sync/index.js +6 -0
- package/dist/core/auto-sync/path-security.d.ts +9 -0
- package/dist/core/auto-sync/path-security.js +251 -0
- package/dist/core/auto-sync/repo.d.ts +1 -0
- package/dist/core/auto-sync/repo.js +6 -0
- package/dist/core/auto-sync/runner.d.ts +44 -0
- package/dist/core/auto-sync/runner.js +428 -0
- package/dist/core/auto-sync/starter.d.ts +50 -0
- package/dist/core/auto-sync/starter.js +497 -0
- package/dist/core/auto-sync/state.d.ts +37 -0
- package/dist/core/auto-sync/state.js +119 -0
- package/dist/core/group/config-parser.js +8 -1
- package/dist/core/group/cross-impact.d.ts +3 -0
- package/dist/core/group/cross-impact.js +36 -4
- package/dist/core/group/extractors/http-patterns/java.js +22 -8
- package/dist/core/group/extractors/http-patterns/kotlin.js +86 -31
- package/dist/core/group/extractors/http-patterns/node.js +145 -8
- package/dist/core/group/extractors/http-patterns/python.js +11 -7
- package/dist/core/group/extractors/http-route-extractor.js +63 -17
- package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
- package/dist/core/group/group-lock.js +5 -6
- package/dist/core/group/normalization.d.ts +38 -1
- package/dist/core/group/normalization.js +59 -3
- package/dist/core/group/service.d.ts +11 -0
- package/dist/core/group/service.js +13 -1
- package/dist/core/group/storage.js +2 -2
- package/dist/core/group/sync.d.ts +24 -1
- package/dist/core/group/sync.js +46 -8
- package/dist/core/group/types.d.ts +13 -0
- package/dist/core/incremental/derived-writeback.d.ts +36 -0
- package/dist/core/incremental/derived-writeback.js +68 -0
- package/dist/core/incremental/spring-config-drift.d.ts +12 -0
- package/dist/core/incremental/spring-config-drift.js +46 -0
- package/dist/core/incremental/subgraph-extract.d.ts +7 -4
- package/dist/core/incremental/subgraph-extract.js +30 -5
- package/dist/core/ingestion/asyncapi/document.d.ts +226 -0
- package/dist/core/ingestion/asyncapi/document.js +758 -0
- package/dist/core/ingestion/asyncapi/protocol.d.ts +79 -0
- package/dist/core/ingestion/asyncapi/protocol.js +203 -0
- package/dist/core/ingestion/call-extractors/configs/zig.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/zig.js +4 -0
- package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +100 -0
- package/dist/core/ingestion/call-extractors/zig-static-gating.js +319 -0
- package/dist/core/ingestion/call-processor.js +3 -1
- package/dist/core/ingestion/class-extractors/configs/zig.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/zig.js +32 -0
- package/dist/core/ingestion/class-extractors/generic.js +4 -1
- package/dist/core/ingestion/class-types.d.ts +6 -2
- package/dist/core/ingestion/destination-key.d.ts +59 -0
- package/dist/core/ingestion/destination-key.js +61 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +6 -2
- package/dist/core/ingestion/entry-point-scoring.js +8 -44
- package/dist/core/ingestion/export-detection.d.ts +37 -0
- package/dist/core/ingestion/export-detection.js +60 -0
- package/dist/core/ingestion/field-extractors/configs/zig.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/zig.js +49 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/generic.js +12 -1
- package/dist/core/ingestion/frameworks/spring/actuator-runtime.d.ts +26 -0
- package/dist/core/ingestion/frameworks/spring/actuator-runtime.js +843 -0
- package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +5 -0
- package/dist/core/ingestion/frameworks/spring/analysis-features.js +9 -0
- package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +8 -1
- package/dist/core/ingestion/frameworks/spring/argument-facts.d.ts +80 -0
- package/dist/core/ingestion/frameworks/spring/argument-facts.js +85 -0
- package/dist/core/ingestion/frameworks/spring/config-bindings.d.ts +1 -0
- package/dist/core/ingestion/frameworks/spring/config-bindings.js +2 -1
- package/dist/core/ingestion/frameworks/spring/destinations.d.ts +539 -0
- package/dist/core/ingestion/frameworks/spring/destinations.js +840 -0
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
- package/dist/core/ingestion/frameworks/spring/message-producers.d.ts +78 -0
- package/dist/core/ingestion/frameworks/spring/message-producers.js +90 -0
- package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +29 -0
- package/dist/core/ingestion/frameworks/spring/vendor-prefixes.d.ts +7 -0
- package/dist/core/ingestion/frameworks/spring/vendor-prefixes.js +22 -0
- package/dist/core/ingestion/import-resolvers/configs/zig.d.ts +13 -0
- package/dist/core/ingestion/import-resolvers/configs/zig.js +26 -0
- package/dist/core/ingestion/import-resolvers/zig.d.ts +34 -0
- package/dist/core/ingestion/import-resolvers/zig.js +193 -0
- package/dist/core/ingestion/language-config.d.ts +177 -0
- package/dist/core/ingestion/language-config.js +719 -0
- package/dist/core/ingestion/language-provider.d.ts +178 -1
- package/dist/core/ingestion/language-provider.js +22 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java/analysis-features.d.ts +1 -1
- package/dist/core/ingestion/languages/java/analysis-features.js +14 -6
- package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +8 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.js +34 -0
- package/dist/core/ingestion/languages/java/captures.js +22 -1
- package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
- package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/java/spring-actuator.d.ts +2 -0
- package/dist/core/ingestion/languages/java/spring-actuator.js +62 -0
- package/dist/core/ingestion/languages/java/spring-di.d.ts +17 -1
- package/dist/core/ingestion/languages/java/spring-di.js +44 -2
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
- package/dist/core/ingestion/languages/java/spring-message-producers.d.ts +20 -0
- package/dist/core/ingestion/languages/java/spring-message-producers.js +83 -0
- package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +11 -1
- package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +13 -3
- package/dist/core/ingestion/languages/java.js +18 -8
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
- package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
- package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +15 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +50 -0
- package/dist/core/ingestion/languages/kotlin/captures.js +24 -1
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
- package/dist/core/ingestion/languages/kotlin/scope-resolver.js +4 -0
- package/dist/core/ingestion/languages/kotlin/spring-actuator.d.ts +4 -0
- package/dist/core/ingestion/languages/kotlin/spring-actuator.js +172 -0
- package/dist/core/ingestion/languages/kotlin/spring-config-bindings.d.ts +18 -0
- package/dist/core/ingestion/languages/kotlin/spring-config-bindings.js +411 -0
- package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +51 -1
- package/dist/core/ingestion/languages/kotlin/spring-di.js +86 -6
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
- package/dist/core/ingestion/languages/kotlin/spring-message-producers.d.ts +17 -0
- package/dist/core/ingestion/languages/kotlin/spring-message-producers.js +119 -0
- package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +16 -0
- package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +77 -2
- package/dist/core/ingestion/languages/kotlin.js +24 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/rust/query.js +8 -8
- package/dist/core/ingestion/languages/zig/captures.d.ts +260 -0
- package/dist/core/ingestion/languages/zig/captures.js +2070 -0
- package/dist/core/ingestion/languages/zig/index.d.ts +4 -0
- package/dist/core/ingestion/languages/zig/index.js +4 -0
- package/dist/core/ingestion/languages/zig/interpret.d.ts +36 -0
- package/dist/core/ingestion/languages/zig/interpret.js +176 -0
- package/dist/core/ingestion/languages/zig/query.d.ts +3 -0
- package/dist/core/ingestion/languages/zig/query.js +426 -0
- package/dist/core/ingestion/languages/zig/range-binding.d.ts +37 -0
- package/dist/core/ingestion/languages/zig/range-binding.js +373 -0
- package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +12 -0
- package/dist/core/ingestion/languages/zig/scope-resolver.js +58 -0
- package/dist/core/ingestion/languages/zig/simple-hooks.d.ts +25 -0
- package/dist/core/ingestion/languages/zig/simple-hooks.js +143 -0
- package/dist/core/ingestion/languages/zig.d.ts +32 -0
- package/dist/core/ingestion/languages/zig.js +130 -0
- package/dist/core/ingestion/method-extractors/configs/zig.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/zig.js +120 -0
- package/dist/core/ingestion/method-extractors/generic.js +16 -4
- package/dist/core/ingestion/method-types.d.ts +10 -4
- package/dist/core/ingestion/model/field-registry.d.ts +3 -3
- package/dist/core/ingestion/model/field-registry.js +11 -7
- package/dist/core/ingestion/model/method-registry.d.ts +3 -3
- package/dist/core/ingestion/model/method-registry.js +13 -8
- package/dist/core/ingestion/model/registration-table.js +3 -0
- package/dist/core/ingestion/model/type-registry.js +8 -4
- package/dist/core/ingestion/pipeline-phases/di.js +47 -14
- 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/parse-impl.d.ts +7 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +79 -91
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +2 -1
- package/dist/core/ingestion/pipeline-phases/processes.js +39 -4
- package/dist/core/ingestion/pipeline-phases/routes.js +27 -18
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
- package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
- package/dist/core/ingestion/pipeline-phases/scan.js +82 -1
- package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +8 -0
- package/dist/core/ingestion/pipeline-phases/spring-destinations.d.ts +123 -0
- package/dist/core/ingestion/pipeline-phases/spring-destinations.js +688 -0
- package/dist/core/ingestion/pipeline.d.ts +30 -1
- package/dist/core/ingestion/pipeline.js +52 -14
- package/dist/core/ingestion/resolve-references.js +5 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +16 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.js +10 -0
- package/dist/core/ingestion/route-extractors/java-const-resolver.d.ts +27 -1
- package/dist/core/ingestion/route-extractors/java-const-resolver.js +161 -13
- package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +12 -18
- package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +214 -33
- package/dist/core/ingestion/route-extractors/kotlin-spring.d.ts +13 -0
- package/dist/core/ingestion/route-extractors/kotlin-spring.js +259 -0
- package/dist/core/ingestion/route-extractors/python-decorator-handler.d.ts +6 -0
- package/dist/core/ingestion/route-extractors/python-decorator-handler.js +16 -0
- package/dist/core/ingestion/route-extractors/spring-shared.d.ts +30 -1
- package/dist/core/ingestion/route-extractors/spring-shared.js +77 -9
- package/dist/core/ingestion/route-extractors/spring.js +9 -5
- package/dist/core/ingestion/scope-extractor.js +8 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +62 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +2 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +28 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +11 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +23 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +24 -3
- package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +11 -1
- package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +26 -6
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +11 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +29 -3
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +8 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +35 -9
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -4
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +277 -9
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +7 -3
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +43 -9
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +71 -10
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
- package/dist/core/ingestion/tree-sitter-queries.js +161 -0
- package/dist/core/ingestion/type-extractors/zig.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/zig.js +17 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +40 -1
- package/dist/core/ingestion/utils/ast-helpers.js +124 -6
- package/dist/core/ingestion/utils/callable-flow-captures.d.ts +9 -0
- package/dist/core/ingestion/utils/callable-flow-captures.js +75 -14
- package/dist/core/ingestion/utils/test-file-path.d.ts +28 -0
- package/dist/core/ingestion/utils/test-file-path.js +87 -0
- package/dist/core/ingestion/variable-extractors/configs/zig.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/zig.js +71 -0
- package/dist/core/ingestion/workers/parse-worker.js +85 -14
- package/dist/core/install-context.d.ts +4 -0
- package/dist/core/install-context.js +152 -0
- package/dist/core/lbug/csv-generator.d.ts +10 -1
- package/dist/core/lbug/csv-generator.js +92 -22
- package/dist/core/lbug/graph-emit-sink.d.ts +3 -2
- package/dist/core/lbug/graph-emit-sink.js +4 -2
- package/dist/core/lbug/lbug-adapter.d.ts +100 -5
- package/dist/core/lbug/lbug-adapter.js +236 -24
- package/dist/core/lbug/schema.d.ts +6 -5
- package/dist/core/lbug/schema.js +84 -22
- package/dist/core/net/url-guard.d.ts +6 -0
- package/dist/core/net/url-guard.js +147 -0
- package/dist/core/package-version.d.ts +5 -0
- package/dist/core/package-version.js +10 -0
- package/dist/core/run-analyze.d.ts +21 -4
- package/dist/core/run-analyze.js +426 -117
- package/dist/core/search/fts-indexes.d.ts +19 -1
- package/dist/core/search/fts-indexes.js +28 -1
- package/dist/core/tree-sitter/parser-loader.js +16 -0
- package/dist/core/update-cache.d.ts +39 -0
- package/dist/core/update-cache.js +141 -0
- package/dist/core/update-check.d.ts +31 -0
- package/dist/core/update-check.js +274 -0
- package/dist/mcp/local/local-backend.d.ts +27 -13
- package/dist/mcp/local/local-backend.js +148 -60
- package/dist/mcp/repository-policy.d.ts +5 -1
- package/dist/mcp/repository-policy.js +48 -4
- package/dist/mcp/resources.js +10 -1
- package/dist/mcp/server.js +8 -9
- package/dist/mcp/tools.js +32 -22
- package/dist/server/analyze-launch.d.ts +2 -0
- package/dist/server/analyze-launch.js +2 -0
- package/dist/server/analyze-worker-protocol.d.ts +6 -1
- package/dist/server/analyze-worker.d.ts +1 -0
- package/dist/server/analyze-worker.js +38 -20
- package/dist/server/api.d.ts +5 -1
- package/dist/server/api.js +105 -88
- package/dist/server/git-clone.d.ts +40 -16
- package/dist/server/git-clone.js +253 -199
- package/dist/server/grep-params.d.ts +18 -0
- package/dist/server/grep-params.js +83 -0
- package/dist/server/grep-scan.d.ts +23 -0
- package/dist/server/grep-scan.js +107 -0
- package/dist/server/grep-worker.d.ts +1 -0
- package/dist/server/grep-worker.js +12 -0
- package/dist/server/mcp-http.d.ts +8 -0
- package/dist/server/mcp-http.js +16 -1
- package/dist/server/update-controller.d.ts +29 -0
- package/dist/server/update-controller.js +82 -0
- package/dist/storage/file-lock.d.ts +15 -0
- package/dist/storage/file-lock.js +155 -0
- package/dist/storage/fs-atomic.d.ts +24 -0
- package/dist/storage/fs-atomic.js +86 -2
- package/dist/storage/global-dir.d.ts +2 -0
- package/dist/storage/global-dir.js +6 -0
- package/dist/storage/parse-cache.d.ts +45 -7
- package/dist/storage/parse-cache.js +189 -42
- package/dist/storage/parsedfile-store.d.ts +41 -60
- package/dist/storage/parsedfile-store.js +247 -171
- package/dist/storage/repo-manager.d.ts +10 -4
- package/dist/storage/repo-manager.js +35 -25
- package/dist/storage/repo-meta.d.ts +34 -0
- package/dist/storage/v8-sidecar.d.ts +48 -0
- package/dist/storage/v8-sidecar.js +347 -0
- package/dist/types/pipeline.d.ts +14 -0
- package/dist/utils/process-identity.d.ts +2 -0
- package/dist/utils/process-identity.js +35 -0
- package/package.json +10 -4
- package/scripts/assert-web-assets.mjs +35 -0
- package/scripts/build-web.js +72 -0
- package/scripts/build.js +31 -28
- package/scripts/cross-platform-tests.ts +5 -0
- package/skills/gitnexus-cli.md +5 -1
- package/web/assets/ProcessFlowModal-DAeX2BHW.js +24 -0
- package/web/assets/{abnfDiagram-QNN2D4NM-Cq2djWWe.js → abnfDiagram-PAR5IXJI-J3OdeHLl.js} +1 -1
- package/web/assets/{agent-BWAXPjC5.js → agent-BMRqXorA.js} +6 -4
- package/web/assets/architecture-7GRP2DOG-M7G3SWCO-G3sWyX6M.js +1 -0
- package/web/assets/architectureDiagram-2PNWDENW-ysKcRlGT.js +36 -0
- package/web/assets/blockDiagram-PUWGUBOA-oLXTa1lY.js +129 -0
- package/web/assets/c4Diagram-3NLNHZP5-D3iu4eC0.js +38 -0
- package/web/assets/{chunk-2SREHG4O-Dip42xOq.js → chunk-2NMSUPJH-B24w-wng.js} +1 -1
- package/web/assets/{chunk-VJFVEZTB-CX66F2ep.js → chunk-32MCUW2P-ByUyt0Sa.js} +3 -3
- package/web/assets/{chunk-4RFN2BYJ-DHHWuC-n.js → chunk-3NPNU5GP-DocZ07Sg.js} +1 -1
- package/web/assets/{chunk-XNMVGMAZ-Cg_LnPtj.js → chunk-3SMGFRGC-CAu4JRLv.js} +1 -1
- package/web/assets/{chunk-W44A43WB-BN8EQc_u.js → chunk-4CAJECRU-DRYMwG9z.js} +1 -1
- package/web/assets/chunk-4OADUZ5Q-CxrSvOwZ.js +10 -0
- package/web/assets/{chunk-DMV4VAQV-Bn6WXDSX.js → chunk-5IQCK5RY-CCMhl4so.js} +2 -2
- package/web/assets/{chunk-RRFMTAIC-CMN0wcYA.js → chunk-5MVFYWFU-Db9KvrYO.js} +1 -1
- package/web/assets/chunk-CLS4B6BI-Uvx5QNsu.js +168 -0
- package/web/assets/chunk-DZP67EKU-B3Mt380j.js +70 -0
- package/web/assets/{chunk-6L755F7B-CMhLBxi2.js → chunk-EICP7K5X-CqwE9hui.js} +1 -1
- package/web/assets/{chunk-BI6VK774-D0oaWk8V.js → chunk-GDY2UAB4-CtQQR0a8.js} +1 -1
- package/web/assets/chunk-H7VHZCWX-CE1nAmt6.js +1 -0
- package/web/assets/chunk-IJBDOHL6-BUKtY6oF.js +1 -0
- package/web/assets/{chunk-OIYT25JQ-CQ6KOZGX.js → chunk-JFFVMJIA-0fJenqia.js} +1 -1
- package/web/assets/{chunk-HNC4WDU7-DoU3KnA7.js → chunk-JZGDI5JK-BeTMSSkJ.js} +1 -1
- package/web/assets/{chunk-EQFTRU2I-1ix7TzW9.js → chunk-KD6SPIPN-Qp1Pflah.js} +1 -1
- package/web/assets/chunk-NF366YI6-CpSpya3n.js +231 -0
- package/web/assets/chunk-NUDPKBEF-CU1-BQIW.js +34 -0
- package/web/assets/{chunk-SATU7PGQ-B1zhgOvc.js → chunk-OJINMHTS-zOCQe4eI.js} +1 -1
- package/web/assets/{chunk-E5QJAATJ-dh-5CtIV.js → chunk-Q6NGPIAH-yyBomb63.js} +1 -1
- package/web/assets/chunk-QJSWEUOL-D_rf9gPD.js +128 -0
- package/web/assets/chunk-R7JDMUKV-CRoqKpD6.js +1 -0
- package/web/assets/{chunk-VE5CLXGZ-C1vB7wpY.js → chunk-RCXAM2GK-CUqrWQDV.js} +1 -1
- package/web/assets/{chunk-66DQ2XMT-CM0xjFAN.js → chunk-RT4MAWR3-zXy9-Lvt.js} +1 -1
- package/web/assets/{chunk-TZUXEDM2-B7p9AcTw.js → chunk-SFMINX4W-J2UrvMpD.js} +1 -1
- package/web/assets/{chunk-LORZLZWG-B57uGw2N.js → chunk-SLDQ463B-CHY6e_bQ.js} +2 -2
- package/web/assets/{chunk-FWYVLQTC-BgDItRzJ.js → chunk-TCEHX7R3-CXPDEVbm.js} +1 -1
- package/web/assets/{chunk-224SPVON-B8gxtXpy.js → chunk-VQC2EELJ-Dc8fIy4R.js} +1 -1
- package/web/assets/chunk-VRZKL3EH-DB3KixTQ.js +1 -0
- package/web/assets/{chunk-A7VWPJGB-B4O3oQdQ.js → chunk-VTHDQ7BV-b7FsPQZO.js} +1 -1
- package/web/assets/chunk-VY2OSTJR-Dkgc3Brg.js +2 -0
- package/web/assets/chunk-WUBWJARI-7FwILJ3k.js +2 -0
- package/web/assets/{chunk-D6VWDJW2-BxcBdZ9c.js → chunk-YBMQ5JGJ-BSnyWZmd.js} +1 -1
- package/web/assets/chunk-YDMLL4PJ-IHhMngTz.js +2 -0
- package/web/assets/chunk-Z6V44ARX-BjCEsF0Q.js +62 -0
- package/web/assets/chunk-ZJJ7VQJP-Dz089b-K.js +321 -0
- package/web/assets/chunk-ZNLK7HRR-B0iFpmW4.js +1 -0
- package/web/assets/chunk-ZQN7UHIL-Cy7KAjQt.js +1 -0
- package/web/assets/{chunk-L5GCOVLC-CE2w8Nm_.js → chunk-ZS6VBONO-DBjJO0w9.js} +2 -2
- package/web/assets/{chunk-NV3KIAZN-5yvcpMiu.js → chunk-ZUNWM646-DNRJyNPe.js} +1 -1
- package/web/assets/classDiagram-FGAMPOII-C5Ibyode.js +1 -0
- package/web/assets/classDiagram-v2-AI5RJEK4-C5Ibyode.js +1 -0
- package/web/assets/{cose-bilkent-RZA4XIPR-BHpK4Crp.js → cose-bilkent-Q72C2OJ3-DMeFXnK8.js} +1 -1
- package/web/assets/cynefin-OW5HDTMX-6OCTWOGG-vlqakYcO.js +1 -0
- package/web/assets/{cynefinDiagram-TR7LZUCG-C3Iar9lZ.js → cynefinDiagram-2OOWUY4M-BY6DZkTt.js} +1 -1
- package/web/assets/dagre-MPVFI544-DWO70fy_.js +4 -0
- package/web/assets/diagram-AE4NWXFW-f7JRVRUL.js +24 -0
- package/web/assets/{diagram-L7DVE3PN-GIRyVUtt.js → diagram-BUJID5UG-BXZv1rCS.js} +2 -2
- package/web/assets/{diagram-JCD7PQ7C-DC5-9FRr.js → diagram-C44CZ5NI-CRNfvjCq.js} +1 -1
- package/web/assets/diagram-EYOLEXXS-DZOjKQVE.js +30 -0
- package/web/assets/diagram-P7B6PWOJ-D3SQmw3_.js +3 -0
- package/web/assets/{ebnfDiagram-DRHRQJWO-D8_CI4ZF.js → ebnfDiagram-FTPZ3PV6-jKWpIDN2.js} +1 -1
- package/web/assets/erDiagram-H3ERR26V-BqJntEuJ.js +99 -0
- package/web/assets/eventmodeling-NTZA5JFV-WKSWQHFS-Bq4FqtBL.js +1 -0
- package/web/assets/flowDiagram-YHGXBVSY-Dnlk2bEu.js +1 -0
- package/web/assets/ganttDiagram-RWFTB7ED-Bl6Y1K5T.js +292 -0
- package/web/assets/gitGraph-4MIJSDKK-JH2HKYUO-B3yc4GkH.js +1 -0
- package/web/assets/gitGraphDiagram-MAR6GKOP-CkNvsrdw.js +106 -0
- package/web/assets/{index-CJDpyH-n.css → index-C2_aej58.css} +1 -1
- package/web/assets/{index-DDRie6_8.js → index-Dj3vQOHK.js} +51 -51
- package/web/assets/info-A6RAGUB7-3TX3OFWW-BM3N5_Dv.js +1 -0
- package/web/assets/infoDiagram-LV547AFH-COkFutAf.js +2 -0
- package/web/assets/{ishikawaDiagram-4JDSWWDM-DmqWtjkl.js → ishikawaDiagram-PUJQGNDS-Bb8FylSR.js} +2 -2
- package/web/assets/{journeyDiagram-QUS3FAKT-XAzVzrQW.js → journeyDiagram-EW5QJT4W-BzoHUBhM.js} +4 -4
- package/web/assets/{kanban-definition-D4L7SQKK-YurRnmYn.js → kanban-definition-JSQERAZB-Uy2Pi0yQ.js} +4 -4
- package/web/assets/katex-CZ4GXH2S-C0Fcou7U.js +257 -0
- package/web/assets/{mindmap-definition-66QP5TD6-ChrWWnil.js → mindmap-definition-VBJCLQLM-B-CnOnt_.js} +3 -3
- package/web/assets/packet-AYTQ26CC-MKII6P3Y-Bd52ZE3V.js +1 -0
- package/web/assets/{pegDiagram-YKIO5C6U-QGeQLEq2.js → pegDiagram-7WILOQTJ-D9sneKXg.js} +1 -1
- package/web/assets/pie-WAS4IAKB-6B4OPMJ5-DW5Xy69Z.js +1 -0
- package/web/assets/pieDiagram-EFA7CBSB-Dx2NQofL.js +39 -0
- package/web/assets/{quadrantDiagram-VO5QLCXA-B7098wSc.js → quadrantDiagram-WARVYA7P-DPKL9_0A.js} +3 -3
- package/web/assets/radar-RG4KPBEZ-UPTR3XUL-CkzibT2h.js +1 -0
- package/web/assets/railroad-74A4TZTK-MEFUPFMS-D9zF-mDN.js +1 -0
- package/web/assets/railroad-abnf-HS5TGJTU-R5YG2S5D-ruacjUbX.js +1 -0
- package/web/assets/railroad-ebnf-LZEXJU2U-G5QEX2L6-S1zc5OiO.js +1 -0
- package/web/assets/railroad-peg-WCYAUIDC-H5I2ZRY2-Cbcr0XzY.js +1 -0
- package/web/assets/{railroadDiagram-M77LTMSB-AXu4vxk_.js → railroadDiagram-5NIQVNLQ-DsweTo14.js} +1 -1
- package/web/assets/{requirementDiagram-RASO5HTO-B-l8InpK.js → requirementDiagram-SXREGOKZ-CXwdNSVR.js} +4 -4
- package/web/assets/{sankeyDiagram-F32LOJ6L-Kd7zjJgX.js → sankeyDiagram-VV74M2VP-CA6jtP9P.js} +3 -3
- package/web/assets/{sequenceDiagram-URATNSBD-BlcdY9DS.js → sequenceDiagram-52ZFIFDL-k_5rEbzm.js} +15 -15
- package/web/assets/src-Df5C1Nz4.js +1 -0
- package/web/assets/stateDiagram-3QAKPIVI-BejHTf31.js +1 -0
- package/web/assets/stateDiagram-v2-HN2P4RDZ-BrfoRuOc.js +1 -0
- package/web/assets/swimlanes-QLVP42QD-CQSVO5D6.js +1 -0
- package/web/assets/swimlanesDiagram-JKPDBFCN-DrWB9kcD.js +8 -0
- package/web/assets/{timeline-definition-WJXWRH4I-DUfp1R3E.js → timeline-definition-GS45XAXW-C0Ks8rPP.js} +4 -4
- package/web/assets/treeView-Q6P3EWNA-ZRJGANNQ-DmKOCo6L.js +1 -0
- package/web/assets/treemap-WGGIJYW6-K64VN5AS-DO3xY92U.js +1 -0
- package/web/assets/{vennDiagram-K3VCMQLI-Bj-FN-XS.js → vennDiagram-QIQU2ZRJ-rgPiS837.js} +3 -3
- package/web/assets/wardley-WFR3VGLG-D67OWCCH-DiDPBNft.js +1 -0
- package/web/assets/wardleyDiagram-U6YNM66N-DRVr7SPi.js +78 -0
- package/web/assets/{xychartDiagram-5VRUVLCW-CK_rbSWE.js → xychartDiagram-XJAZEDNM-COtlZzYb.js} +3 -3
- package/web/index.html +19 -17
- package/web/assets/ProcessFlowModal-Badh2Pbg.js +0 -24
- package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +0 -1
- package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +0 -36
- package/web/assets/blockDiagram-54WLDVWM-Dg_hrRWE.js +0 -132
- package/web/assets/c4Diagram-X2LASM7V-CnhoT4aV.js +0 -10
- package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +0 -127
- package/web/assets/chunk-5VCL7Z4A-CQ-2dYWj.js +0 -1
- package/web/assets/chunk-6BELYETK-ChsYtfHW.js +0 -1
- package/web/assets/chunk-7CWYLC5S-DRgGkkpF.js +0 -2
- package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +0 -32
- package/web/assets/chunk-KRXBNO2N-C-UNuKKf.js +0 -1
- package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +0 -321
- package/web/assets/chunk-LIEV3EAG-pyKGssV3.js +0 -1
- package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +0 -62
- package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +0 -10
- package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +0 -156
- package/web/assets/chunk-SZD42YQK-ijyKKVr1.js +0 -70
- package/web/assets/chunk-TFPGFQPA-Dj6a-0pC.js +0 -231
- package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +0 -2
- package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +0 -1
- package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +0 -1
- package/web/assets/chunk-WJBAP47W-BckNkuEN.js +0 -1
- package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +0 -1
- package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +0 -1
- package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +0 -1
- package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +0 -4
- package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +0 -30
- package/web/assets/diagram-KFBZAVDW-Dfmf_4ti.js +0 -3
- package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +0 -24
- package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +0 -85
- package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +0 -1
- package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +0 -1
- package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +0 -292
- package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +0 -1
- package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +0 -106
- package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +0 -1
- package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +0 -2
- package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +0 -257
- package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +0 -1
- package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +0 -1
- package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +0 -39
- package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +0 -1
- package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +0 -1
- package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +0 -1
- package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +0 -1
- package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +0 -1
- package/web/assets/src-C5sx_gMp.js +0 -1
- package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +0 -1
- package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +0 -1
- package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +0 -2
- package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +0 -8
- package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +0 -1
- package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +0 -1
- package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +0 -1
- package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +0 -78
- /package/web/assets/{sizeCapture-5GORAGP4-BEqQzsxs.js → sizeCapture-FGA5AGYP-BEqQzsxs.js} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query-parameter parsing for GET /api/grep.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from api.ts so the contract is unit-testable without pulling
|
|
5
|
+
* Express + the LadybugDB native adapter into the test run (same rationale
|
|
6
|
+
* as the #2790 helper extraction).
|
|
7
|
+
*
|
|
8
|
+
* Contract fix (Patch 12): the grep tool schema in gitnexus-web has always
|
|
9
|
+
* promised regex search with an optional path-substring filter and
|
|
10
|
+
* case-sensitivity control, but the handler used to escapeRegExp() every
|
|
11
|
+
* pattern into a literal substring — an agent asking for "sign|Sign" got
|
|
12
|
+
* zero hits and concluded the code didn't exist, and the schema's own
|
|
13
|
+
* example ("console\.log") could never match. This restores the promised
|
|
14
|
+
* semantics. Matching runs in a worker_threads worker (`grep-worker.ts`) so
|
|
15
|
+
* `terminate()` can interrupt a catastrophic `regex.test()` when the
|
|
16
|
+
* wall-clock budget expires; the parent event loop stays responsive.
|
|
17
|
+
* Bounded mitigations:
|
|
18
|
+
* - pattern length cap (200 chars, unchanged from the literal-only era)
|
|
19
|
+
* - line-by-line matching (each regex.test call sees one source line)
|
|
20
|
+
* - a wall-clock budget the parent enforces via worker terminate()
|
|
21
|
+
* - result cap unchanged (limit, max 200)
|
|
22
|
+
* - literal=1 opt-out restores the old escaped-substring immunity
|
|
23
|
+
*/
|
|
24
|
+
import { assertString, escapeRegExp, BadRequestError } from './validation.js';
|
|
25
|
+
/** Hard cap on pattern length — unchanged from the literal-only era. */
|
|
26
|
+
export const GREP_PATTERN_MAX_LENGTH = 200;
|
|
27
|
+
/** Wall-clock budget for one /api/grep call; parent terminate()s the scan worker. */
|
|
28
|
+
export const GREP_TIME_BUDGET_MS = 5_000;
|
|
29
|
+
export const GREP_DEFAULT_LIMIT = 50;
|
|
30
|
+
export const GREP_MAX_LIMIT = 200;
|
|
31
|
+
const isFlagTrue = (value, name) => {
|
|
32
|
+
const s = assertString(value ?? '', name).toLowerCase();
|
|
33
|
+
return s === '1' || s === 'true';
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Parse /api/grep query parameters into a ready-to-use regex + filters.
|
|
37
|
+
* Throws BadRequestError (mapped to HTTP 400 by statusFromError) on
|
|
38
|
+
* missing/over-long patterns or invalid regex syntax.
|
|
39
|
+
*/
|
|
40
|
+
export function parseGrepQuery(query) {
|
|
41
|
+
if (query.pattern === undefined) {
|
|
42
|
+
throw new BadRequestError('Missing "pattern" query parameter');
|
|
43
|
+
}
|
|
44
|
+
const pattern = assertString(query.pattern, 'pattern');
|
|
45
|
+
if (pattern.length === 0) {
|
|
46
|
+
throw new BadRequestError('Missing "pattern" query parameter');
|
|
47
|
+
}
|
|
48
|
+
if (pattern.length > GREP_PATTERN_MAX_LENGTH) {
|
|
49
|
+
throw new BadRequestError(`Pattern too long (max ${GREP_PATTERN_MAX_LENGTH} characters)`);
|
|
50
|
+
}
|
|
51
|
+
// Regex semantics by default — what the tool schema always promised.
|
|
52
|
+
// literal=1 opts back into the escaped-substring behaviour of the
|
|
53
|
+
// literal-only era for callers that want it verbatim.
|
|
54
|
+
const caseSensitive = isFlagTrue(query.caseSensitive, 'caseSensitive');
|
|
55
|
+
const flags = caseSensitive ? '' : 'i';
|
|
56
|
+
let regex;
|
|
57
|
+
try {
|
|
58
|
+
// Deliberately no 'g' flag: the handler tests line-by-line and a
|
|
59
|
+
// stateful lastIndex across lines would skip matches (the old handler
|
|
60
|
+
// had to reset it manually). No 'm' either: each test receives a
|
|
61
|
+
// single line, so ^/$ already anchor at string boundaries — 'm'
|
|
62
|
+
// would be a no-op.
|
|
63
|
+
if (isFlagTrue(query.literal, 'literal')) {
|
|
64
|
+
regex = new RegExp(escapeRegExp(pattern), flags);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Intentional: /api/grep advertises real regex (see file header + SECURITY.md).
|
|
68
|
+
// ReDoS is mitigated by running the scan in a worker and terminate()-ing it.
|
|
69
|
+
// codeql[js/regex-injection]
|
|
70
|
+
regex = new RegExp(pattern, flags);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new BadRequestError('Invalid regex pattern');
|
|
75
|
+
}
|
|
76
|
+
// Path-substring filter, case-insensitive ("Controller.java", "src/api").
|
|
77
|
+
const fileFilter = assertString(query.fileFilter ?? '', 'fileFilter').toLowerCase();
|
|
78
|
+
const parsedLimit = Number(query.limit ?? GREP_DEFAULT_LIMIT);
|
|
79
|
+
const limit = Number.isFinite(parsedLimit)
|
|
80
|
+
? Math.max(1, Math.min(GREP_MAX_LIMIT, Math.trunc(parsedLimit)))
|
|
81
|
+
: GREP_DEFAULT_LIMIT;
|
|
82
|
+
return { regex, fileFilter, limit };
|
|
83
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface GrepHit {
|
|
2
|
+
filePath: string;
|
|
3
|
+
line: number;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GrepScanInput {
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
/** Repo-relative paths already filtered by fileFilter. */
|
|
9
|
+
filePaths: string[];
|
|
10
|
+
pattern: string;
|
|
11
|
+
flags: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
/** Absolute Date.now() deadline. */
|
|
14
|
+
deadlineMs: number;
|
|
15
|
+
}
|
|
16
|
+
export interface GrepScanResult {
|
|
17
|
+
results: GrepHit[];
|
|
18
|
+
timedOut: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type GrepProgress = (partial: GrepScanResult) => void;
|
|
21
|
+
export declare function scanGrepFiles(input: GrepScanInput, onProgress?: GrepProgress): Promise<GrepScanResult>;
|
|
22
|
+
export declare function grepWorkerPath(): string;
|
|
23
|
+
export declare function runGrepScanInWorker(input: GrepScanInput): Promise<GrepScanResult>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem grep scan used by GET /api/grep.
|
|
3
|
+
*
|
|
4
|
+
* Matching runs in a worker_threads worker (see grep-worker.ts) so a
|
|
5
|
+
* catastrophic `regex.test()` can be killed with terminate() when the
|
|
6
|
+
* wall-clock budget expires. The parent event loop stays responsive.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
12
|
+
import { Worker } from 'node:worker_threads';
|
|
13
|
+
export async function scanGrepFiles(input, onProgress) {
|
|
14
|
+
const regex = new RegExp(input.pattern, input.flags);
|
|
15
|
+
const results = [];
|
|
16
|
+
const repoRoot = path.resolve(input.repoRoot);
|
|
17
|
+
const safeRepoRoot = repoRoot.endsWith(path.sep) ? repoRoot : repoRoot + path.sep;
|
|
18
|
+
let timedOut = false;
|
|
19
|
+
files: for (const filePath of input.filePaths) {
|
|
20
|
+
if (results.length >= input.limit)
|
|
21
|
+
break;
|
|
22
|
+
if (Date.now() > input.deadlineMs) {
|
|
23
|
+
timedOut = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
const fullPath = path.resolve(repoRoot, filePath);
|
|
27
|
+
if (!fullPath.startsWith(safeRepoRoot) && fullPath !== repoRoot)
|
|
28
|
+
continue;
|
|
29
|
+
let content;
|
|
30
|
+
try {
|
|
31
|
+
content = await fs.readFile(fullPath, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38
|
+
if (results.length >= input.limit)
|
|
39
|
+
break files;
|
|
40
|
+
if ((i & 255) === 0 && Date.now() > input.deadlineMs) {
|
|
41
|
+
timedOut = true;
|
|
42
|
+
break files;
|
|
43
|
+
}
|
|
44
|
+
regex.lastIndex = 0;
|
|
45
|
+
if (regex.test(lines[i])) {
|
|
46
|
+
results.push({ filePath, line: i + 1, text: lines[i].trim().slice(0, 200) });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
onProgress?.({ results: results.slice(), timedOut });
|
|
50
|
+
}
|
|
51
|
+
return { results, timedOut };
|
|
52
|
+
}
|
|
53
|
+
const _require = createRequire(import.meta.url);
|
|
54
|
+
export function grepWorkerPath() {
|
|
55
|
+
const callerPath = fileURLToPath(import.meta.url);
|
|
56
|
+
const isDev = callerPath.endsWith('.ts');
|
|
57
|
+
return path.join(path.dirname(callerPath), isDev ? 'grep-worker.ts' : 'grep-worker.js');
|
|
58
|
+
}
|
|
59
|
+
export function runGrepScanInWorker(input) {
|
|
60
|
+
const callerPath = fileURLToPath(import.meta.url);
|
|
61
|
+
const isDev = callerPath.endsWith('.ts');
|
|
62
|
+
const tsxHookArgs = isDev
|
|
63
|
+
? ['--import', pathToFileURL(_require.resolve('tsx/esm')).href]
|
|
64
|
+
: [];
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
let settled = false;
|
|
67
|
+
let latest = { results: [], timedOut: false };
|
|
68
|
+
const worker = new Worker(grepWorkerPath(), {
|
|
69
|
+
workerData: input,
|
|
70
|
+
execArgv: tsxHookArgs,
|
|
71
|
+
});
|
|
72
|
+
const finish = (result) => {
|
|
73
|
+
if (settled)
|
|
74
|
+
return;
|
|
75
|
+
settled = true;
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
void worker.terminate();
|
|
78
|
+
resolve(result);
|
|
79
|
+
};
|
|
80
|
+
const remain = Math.max(1, input.deadlineMs - Date.now());
|
|
81
|
+
const timer = setTimeout(() => {
|
|
82
|
+
finish({ results: latest.results, timedOut: true });
|
|
83
|
+
}, remain);
|
|
84
|
+
worker.on('message', (msg) => {
|
|
85
|
+
if (msg.type === 'progress') {
|
|
86
|
+
latest = { results: msg.results, timedOut: msg.timedOut };
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (msg.type === 'done') {
|
|
90
|
+
finish({ results: msg.results, timedOut: msg.timedOut });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
worker.on('error', (err) => {
|
|
94
|
+
if (settled)
|
|
95
|
+
return;
|
|
96
|
+
settled = true;
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
void worker.terminate();
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
worker.on('exit', () => {
|
|
102
|
+
if (settled)
|
|
103
|
+
return;
|
|
104
|
+
finish({ results: latest.results, timedOut: true });
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parentPort, workerData } from 'node:worker_threads';
|
|
2
|
+
if (!parentPort) {
|
|
3
|
+
throw new Error('grep-worker must run as a worker_threads worker');
|
|
4
|
+
}
|
|
5
|
+
const ext = import.meta.url.endsWith('.ts') ? '.ts' : '.js';
|
|
6
|
+
const { scanGrepFiles } = await import(new URL(`./grep-scan${ext}`, import.meta.url).href);
|
|
7
|
+
const port = parentPort;
|
|
8
|
+
const input = workerData;
|
|
9
|
+
const out = await scanGrepFiles(input, (partial) => {
|
|
10
|
+
port.postMessage({ type: 'progress', ...partial });
|
|
11
|
+
});
|
|
12
|
+
port.postMessage({ type: 'done', ...out });
|
|
@@ -9,4 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Express } from 'express';
|
|
11
11
|
import type { LocalBackend } from '../mcp/local/local-backend.js';
|
|
12
|
+
/**
|
|
13
|
+
* Protect serve's /api/mcp route when the shared MCP bearer token is configured.
|
|
14
|
+
*
|
|
15
|
+
* This middleware must be installed before Express's global JSON parser so an
|
|
16
|
+
* unauthenticated request body is rejected before it is parsed. The standalone
|
|
17
|
+
* `gitnexus mcp --http` server resolves the same environment variable.
|
|
18
|
+
*/
|
|
19
|
+
export declare function installServeMcpAuth(app: Express, env?: NodeJS.ProcessEnv): boolean;
|
|
12
20
|
export declare function mountMCPEndpoints(app: Express, backend: LocalBackend): Promise<() => Promise<void>>;
|
package/dist/server/mcp-http.js
CHANGED
|
@@ -7,9 +7,24 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Used by server/api.ts to wire up the full web server.
|
|
9
9
|
*/
|
|
10
|
-
import { createStreamableHttpHandler } from '../mcp/http-transport.js';
|
|
10
|
+
import { createAuthMiddleware, createStreamableHttpHandler, resolveAuthToken, } from '../mcp/http-transport.js';
|
|
11
11
|
import { createMcpRepositoryPolicy } from '../mcp/repository-policy.js';
|
|
12
12
|
import { logger } from '../core/logger.js';
|
|
13
|
+
/**
|
|
14
|
+
* Protect serve's /api/mcp route when the shared MCP bearer token is configured.
|
|
15
|
+
*
|
|
16
|
+
* This middleware must be installed before Express's global JSON parser so an
|
|
17
|
+
* unauthenticated request body is rejected before it is parsed. The standalone
|
|
18
|
+
* `gitnexus mcp --http` server resolves the same environment variable.
|
|
19
|
+
*/
|
|
20
|
+
export function installServeMcpAuth(app, env = process.env) {
|
|
21
|
+
const authToken = resolveAuthToken(undefined, env);
|
|
22
|
+
if (!authToken)
|
|
23
|
+
return false;
|
|
24
|
+
app.use('/api/mcp', createAuthMiddleware(authToken));
|
|
25
|
+
logger.info('Bearer authentication enabled for serve /api/mcp');
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
13
28
|
export async function mountMCPEndpoints(app, backend) {
|
|
14
29
|
const repositoryPolicy = await createMcpRepositoryPolicy(backend);
|
|
15
30
|
const { handler, cleanup } = createStreamableHttpHandler(backend, { repositoryPolicy });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type UpdateState } from '../core/update-check.js';
|
|
2
|
+
export interface ServerInfoResponse {
|
|
3
|
+
version: string;
|
|
4
|
+
launchContext: 'npx' | 'global' | 'local';
|
|
5
|
+
nodeVersion: string;
|
|
6
|
+
latestVersion?: string;
|
|
7
|
+
updateAvailable?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface ServeUpdateControllerDependencies {
|
|
10
|
+
evaluate: (options?: {
|
|
11
|
+
refreshIfStale?: boolean;
|
|
12
|
+
}) => Promise<UpdateState | null>;
|
|
13
|
+
armScheduler: (onState: (state: UpdateState | null) => void) => () => void;
|
|
14
|
+
}
|
|
15
|
+
export interface ServeUpdateController {
|
|
16
|
+
start: () => Promise<void>;
|
|
17
|
+
stop: () => void;
|
|
18
|
+
snapshot: () => UpdateState | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Own the update state for one `serve` process. The route reads snapshot()
|
|
22
|
+
* synchronously; all cache and network work stays on the startup/scheduler path.
|
|
23
|
+
*/
|
|
24
|
+
export declare const createServeUpdateController: (dependencies?: ServeUpdateControllerDependencies) => ServeUpdateController;
|
|
25
|
+
export declare const buildServerInfo: (updateState: UpdateState | null) => ServerInfoResponse;
|
|
26
|
+
export declare const bindServeUpdateControllerLifecycle: (server: {
|
|
27
|
+
once(event: "listening" | "close", listener: () => void): unknown;
|
|
28
|
+
}, controller: ServeUpdateController) => void;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { packageVersion } from '../core/package-version.js';
|
|
2
|
+
import { armUpdateRefreshScheduler, evaluate } from '../core/update-check.js';
|
|
3
|
+
/**
|
|
4
|
+
* Own the update state for one `serve` process. The route reads snapshot()
|
|
5
|
+
* synchronously; all cache and network work stays on the startup/scheduler path.
|
|
6
|
+
*/
|
|
7
|
+
export const createServeUpdateController = (dependencies = {
|
|
8
|
+
evaluate,
|
|
9
|
+
armScheduler: armUpdateRefreshScheduler,
|
|
10
|
+
}) => {
|
|
11
|
+
let updateState = null;
|
|
12
|
+
let stopScheduler;
|
|
13
|
+
let started = false;
|
|
14
|
+
let stopped = false;
|
|
15
|
+
return {
|
|
16
|
+
start: async () => {
|
|
17
|
+
if (started || stopped)
|
|
18
|
+
return;
|
|
19
|
+
started = true;
|
|
20
|
+
try {
|
|
21
|
+
// Cache-only: the scheduler's first cycle owns any stale refresh.
|
|
22
|
+
updateState = await dependencies.evaluate({ refreshIfStale: false });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
updateState = null;
|
|
26
|
+
}
|
|
27
|
+
if (stopped)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
stopScheduler = dependencies.armScheduler((state) => {
|
|
31
|
+
if (state?.updateAvailable !== updateState?.updateAvailable ||
|
|
32
|
+
state?.latestVersion !== updateState?.latestVersion) {
|
|
33
|
+
updateState = state;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Update checks are best-effort and never affect HTTP availability.
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
stop: () => {
|
|
42
|
+
if (stopped)
|
|
43
|
+
return;
|
|
44
|
+
stopped = true;
|
|
45
|
+
try {
|
|
46
|
+
stopScheduler?.();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Shutdown must continue even if notifier cleanup unexpectedly fails.
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
snapshot: () => updateState,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export const buildServerInfo = (updateState) => {
|
|
56
|
+
const execPath = process.env.npm_execpath ?? '';
|
|
57
|
+
const argv0 = process.argv[1] ?? '';
|
|
58
|
+
let launchContext;
|
|
59
|
+
if (execPath.includes('npx') ||
|
|
60
|
+
argv0.includes('_npx') ||
|
|
61
|
+
process.env.npm_config_prefix?.includes('_npx')) {
|
|
62
|
+
launchContext = 'npx';
|
|
63
|
+
}
|
|
64
|
+
else if (argv0.includes('node_modules')) {
|
|
65
|
+
launchContext = 'local';
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
launchContext = 'global';
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
version: packageVersion(),
|
|
72
|
+
launchContext,
|
|
73
|
+
nodeVersion: process.version,
|
|
74
|
+
...(updateState?.updateAvailable && updateState.latestVersion
|
|
75
|
+
? { latestVersion: updateState.latestVersion, updateAvailable: true }
|
|
76
|
+
: {}),
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
export const bindServeUpdateControllerLifecycle = (server, controller) => {
|
|
80
|
+
server.once('listening', () => void controller.start());
|
|
81
|
+
server.once('close', controller.stop);
|
|
82
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FileLockOptions {
|
|
2
|
+
retries?: number;
|
|
3
|
+
retryDelayMs?: number;
|
|
4
|
+
pid?: number;
|
|
5
|
+
processStartTime?: string;
|
|
6
|
+
hostname?: string;
|
|
7
|
+
isProcessAlive?: (pid: number) => boolean;
|
|
8
|
+
readProcessStartTime?: (pid: number) => string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare class FileLockBusyError extends Error {
|
|
11
|
+
readonly lockPath: string;
|
|
12
|
+
constructor(lockPath: string);
|
|
13
|
+
}
|
|
14
|
+
/** Acquire a recoverable cross-process mutex using an atomically published owner file. */
|
|
15
|
+
export declare function acquireFileLock(lockPath: string, options?: FileLockOptions): Promise<() => Promise<void>>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
6
|
+
import { isProcessAlive, readProcessStartTime } from '../utils/process-identity.js';
|
|
7
|
+
const HOSTNAME = os.hostname();
|
|
8
|
+
export class FileLockBusyError extends Error {
|
|
9
|
+
lockPath;
|
|
10
|
+
constructor(lockPath) {
|
|
11
|
+
super(`Lock is already held: ${lockPath}. Confirm no owner process is active, then remove it manually.`);
|
|
12
|
+
this.lockPath = lockPath;
|
|
13
|
+
this.name = 'FileLockBusyError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Acquire a recoverable cross-process mutex using an atomically published owner file. */
|
|
17
|
+
export async function acquireFileLock(lockPath, options = {}) {
|
|
18
|
+
const resolvedPath = path.resolve(lockPath);
|
|
19
|
+
const retries = options.retries ?? 0;
|
|
20
|
+
const retryDelayMs = options.retryDelayMs ?? 50;
|
|
21
|
+
const pid = options.pid ?? process.pid;
|
|
22
|
+
const owner = {
|
|
23
|
+
pid,
|
|
24
|
+
ownerId: crypto.randomUUID(),
|
|
25
|
+
processStartTime: options.processStartTime ?? (options.readProcessStartTime ?? readProcessStartTime)(pid) ?? '',
|
|
26
|
+
hostname: options.hostname ?? HOSTNAME,
|
|
27
|
+
};
|
|
28
|
+
if (!owner.processStartTime) {
|
|
29
|
+
throw new Error(`Unable to determine process start time for file lock owner pid ${owner.pid}.`);
|
|
30
|
+
}
|
|
31
|
+
await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
|
|
32
|
+
const pendingPath = `${resolvedPath}.pending-${owner.ownerId}`;
|
|
33
|
+
await fs.writeFile(pendingPath, `${JSON.stringify(owner)}\n`, { encoding: 'utf-8', flag: 'wx' });
|
|
34
|
+
try {
|
|
35
|
+
for (let attempt = 0;; attempt += 1) {
|
|
36
|
+
try {
|
|
37
|
+
await fs.link(pendingPath, resolvedPath);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (!(await isLockConflict(error, resolvedPath)))
|
|
42
|
+
throw error;
|
|
43
|
+
if (await reclaimStaleLock(resolvedPath, owner, options.isProcessAlive ?? isProcessAlive, options.readProcessStartTime ?? readProcessStartTime)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (attempt >= retries)
|
|
47
|
+
throw new FileLockBusyError(lockPath);
|
|
48
|
+
await sleep(retryDelayMs);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
// The lock is already published by now, but the release closure below is
|
|
54
|
+
// not yet in the caller's hands. Letting a staging-file cleanup error
|
|
55
|
+
// escape would strand a lock nobody can release, so prefer leaking the
|
|
56
|
+
// pending file — its name is per-acquisition, so it can never block anyone.
|
|
57
|
+
await fs.rm(pendingPath, { force: true }).catch(() => { });
|
|
58
|
+
}
|
|
59
|
+
let releasePromise;
|
|
60
|
+
return () => (releasePromise ??= releaseOwnedLock(resolvedPath, owner.ownerId));
|
|
61
|
+
}
|
|
62
|
+
async function reclaimStaleLock(lockPath, guardOwner, ownerIsAlive, getProcessStartTime) {
|
|
63
|
+
const reclaimGuardPath = `${lockPath}.reclaim`;
|
|
64
|
+
let releaseReclaimGuard;
|
|
65
|
+
try {
|
|
66
|
+
releaseReclaimGuard = await acquireFileLock(reclaimGuardPath, {
|
|
67
|
+
pid: guardOwner.pid,
|
|
68
|
+
processStartTime: guardOwner.processStartTime,
|
|
69
|
+
hostname: guardOwner.hostname,
|
|
70
|
+
isProcessAlive: ownerIsAlive,
|
|
71
|
+
readProcessStartTime: getProcessStartTime,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error instanceof FileLockBusyError)
|
|
76
|
+
return false;
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const owner = await readOwner(lockPath);
|
|
81
|
+
if (!owner)
|
|
82
|
+
return false;
|
|
83
|
+
// A pid only means something on the machine that issued it. Asking this
|
|
84
|
+
// kernel about a holder on another host answers about an unrelated process
|
|
85
|
+
// — or nothing — and either way the answer is "stale", which would steal a
|
|
86
|
+
// live lock whenever GITNEXUS_HOME is a shared volume.
|
|
87
|
+
if (owner.hostname !== guardOwner.hostname)
|
|
88
|
+
return false;
|
|
89
|
+
if (ownerIsAlive(owner.pid)) {
|
|
90
|
+
const currentStartTime = getProcessStartTime(owner.pid);
|
|
91
|
+
if (!currentStartTime || currentStartTime === owner.processStartTime)
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
await fs.rm(lockPath, { force: true });
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await releaseReclaimGuard();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function releaseOwnedLock(lockPath, ownerId) {
|
|
102
|
+
const releasePath = `${lockPath}.release-${ownerId}-${crypto.randomUUID()}`;
|
|
103
|
+
if (!(await moveOwnedLock(lockPath, releasePath, ownerId)))
|
|
104
|
+
return;
|
|
105
|
+
await fs.rm(releasePath, { force: true });
|
|
106
|
+
}
|
|
107
|
+
async function moveOwnedLock(lockPath, destinationPath, ownerId) {
|
|
108
|
+
if ((await readOwner(lockPath))?.ownerId !== ownerId)
|
|
109
|
+
return false;
|
|
110
|
+
try {
|
|
111
|
+
await fs.rename(lockPath, destinationPath);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code === 'ENOENT')
|
|
115
|
+
return false;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
if ((await readOwner(destinationPath))?.ownerId === ownerId)
|
|
119
|
+
return true;
|
|
120
|
+
await fs.rename(destinationPath, lockPath).catch(() => { });
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
async function readOwner(lockPath) {
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(await fs.readFile(lockPath, 'utf-8'));
|
|
126
|
+
if (Number.isInteger(parsed.pid) &&
|
|
127
|
+
Number(parsed.pid) > 0 &&
|
|
128
|
+
typeof parsed.ownerId === 'string' &&
|
|
129
|
+
parsed.ownerId &&
|
|
130
|
+
typeof parsed.processStartTime === 'string' &&
|
|
131
|
+
parsed.processStartTime &&
|
|
132
|
+
typeof parsed.hostname === 'string' &&
|
|
133
|
+
parsed.hostname) {
|
|
134
|
+
return parsed;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Invalid or legacy locks fail closed; only verified dead owners are reclaimed.
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
async function isLockConflict(error, lockPath) {
|
|
143
|
+
const code = error.code;
|
|
144
|
+
if (code === 'EEXIST')
|
|
145
|
+
return true;
|
|
146
|
+
if (code !== 'EPERM')
|
|
147
|
+
return false;
|
|
148
|
+
try {
|
|
149
|
+
await fs.access(lockPath);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -28,3 +28,27 @@ export declare function retryRename(src: string, dst: string, attempts?: number)
|
|
|
28
28
|
* retry backoff (and, under a lock, make everyone else wait for it).
|
|
29
29
|
*/
|
|
30
30
|
export declare function writeFileAtomic(targetPath: string, data: string, attempts?: number): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Publish `src` at `dst` without ever writing through the inode currently
|
|
33
|
+
* named by `dst` (#3090). Fast path is `fs.link`. Any failure (EEXIST, EXDEV,
|
|
34
|
+
* EPERM, unsupported hardlinks) copies into a unique sibling and
|
|
35
|
+
* {@link retryRename}s over `dst`. POSIX rename of a file over an existing
|
|
36
|
+
* file unlinks the old name; it does not open or truncate that inode.
|
|
37
|
+
*
|
|
38
|
+
* Invariant: may replace the destination directory entry; must never modify
|
|
39
|
+
* the inode that entry currently references. There is no dest unlink and no
|
|
40
|
+
* in-place `copyFile(src, dst)`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function linkOrCopyFile(src: string, dst: string): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Binary sibling of {@link writeFileAtomic}. Same random tmp + `'wx'` + `0o600`
|
|
45
|
+
* + rename contract; used for optional V8 cache sidecars that cannot go through
|
|
46
|
+
* a UTF-8 `writeFile`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function writeFileAtomicBytes(targetPath: string, data: Uint8Array, attempts?: number): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Sync binary publish for parse workers. Same exclusive-tmp + mode contract as
|
|
51
|
+
* {@link writeFileAtomicBytes}; rename is not retried (the worker is not the
|
|
52
|
+
* Windows multi-reader case `retryRename` exists for).
|
|
53
|
+
*/
|
|
54
|
+
export declare function writeFileAtomicBytesSync(targetPath: string, data: Uint8Array): void;
|