gitnexus 1.6.10-rc.21 → 1.6.10-rc.211
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 +212 -22
- package/dist/_shared/graph/types.d.ts +27 -8
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +4 -3
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +1 -1
- package/dist/_shared/index.js.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 +19 -1
- package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
- package/dist/_shared/integrations/resilient-fetch.js +23 -10
- package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
- package/dist/_shared/language-detection.d.ts.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 +3 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/callable-flow-site.d.ts +110 -0
- package/dist/_shared/scope-resolution/callable-flow-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/callable-flow-site.js +11 -0
- package/dist/_shared/scope-resolution/callable-flow-site.js.map +1 -0
- package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +6 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +316 -37
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +7 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +124 -1
- 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/lookup-core.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js +58 -2
- package/dist/_shared/scope-resolution/registries/lookup-core.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/symbol-definition.d.ts +47 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +297 -5
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +10 -2
- package/dist/cli/ai-context.js +128 -61
- package/dist/cli/analyze-config.d.ts +1 -1
- package/dist/cli/analyze-options.d.ts +131 -0
- package/dist/cli/analyze-options.js +1 -0
- package/dist/cli/analyze.d.ts +25 -114
- package/dist/cli/analyze.js +194 -36
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/detect-changes-format.js +19 -2
- package/dist/cli/doctor.d.ts +23 -0
- package/dist/cli/doctor.js +72 -9
- package/dist/cli/editor-targets.js +6 -0
- package/dist/cli/eval-server.d.ts +23 -1
- package/dist/cli/eval-server.js +128 -9
- package/dist/cli/format-symbol.d.ts +15 -0
- package/dist/cli/format-symbol.js +17 -0
- package/dist/cli/generated-skill.d.ts +17 -0
- package/dist/cli/generated-skill.js +1 -0
- package/dist/cli/group.js +23 -2
- package/dist/cli/help-i18n.js +3 -0
- package/dist/cli/i18n/en.d.ts +17 -8
- package/dist/cli/i18n/en.js +19 -8
- package/dist/cli/i18n/resources.d.ts +26 -8
- package/dist/cli/i18n/zh-CN.d.ts +9 -0
- package/dist/cli/i18n/zh-CN.js +17 -8
- package/dist/cli/index.js +16 -9
- package/dist/cli/lazy-action.d.ts +7 -0
- package/dist/cli/lazy-action.js +27 -0
- package/dist/cli/mcp.js +6 -3
- package/dist/cli/resolve-invocation.d.ts +7 -3
- package/dist/cli/resolve-invocation.js +7 -3
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +30 -7
- package/dist/cli/skill-gen.d.ts +1 -6
- package/dist/cli/skill-gen.js +88 -18
- package/dist/cli/standard-skills.d.ts +76 -0
- package/dist/cli/standard-skills.js +45 -0
- package/dist/cli/status.d.ts +4 -1
- package/dist/cli/status.js +57 -4
- package/dist/cli/tool.d.ts +1 -0
- package/dist/cli/tool.js +75 -7
- package/dist/cli/uninstall.js +7 -2
- package/dist/cli/wiki.d.ts +1 -0
- package/dist/cli/wiki.js +59 -10
- package/dist/config/ignore-service.d.ts +2 -0
- package/dist/config/ignore-service.js +31 -4
- package/dist/core/analysis-features.d.ts +21 -0
- package/dist/core/analysis-features.js +59 -0
- package/dist/core/analyzer-identity.d.ts +154 -0
- package/dist/core/analyzer-identity.js +2158 -0
- package/dist/core/augmentation/engine.js +95 -76
- package/dist/core/embedding-checkpoint.d.ts +143 -0
- package/dist/core/embedding-checkpoint.js +221 -0
- package/dist/core/embedding-count.d.ts +61 -0
- package/dist/core/embedding-count.js +69 -0
- package/dist/core/embeddings/ast-utils.js +9 -1
- package/dist/core/embeddings/embedder.d.ts +3 -2
- package/dist/core/embeddings/embedder.js +8 -5
- package/dist/core/embeddings/embedding-identity.d.ts +11 -0
- package/dist/core/embeddings/embedding-identity.js +24 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +24 -1
- package/dist/core/embeddings/embedding-pipeline.js +341 -19
- package/dist/core/embeddings/http-client.d.ts +10 -2
- package/dist/core/embeddings/http-client.js +282 -47
- package/dist/core/embeddings/node-module-compat.d.ts +4 -2
- package/dist/core/embeddings/node-module-compat.js +4 -2
- package/dist/core/embeddings/onnxruntime-common-resolver.js +5 -5
- package/dist/core/embeddings/onnxruntime-node-resolver.js +2 -2
- package/dist/core/embeddings/runtime-install.js +1 -1
- package/dist/core/git-staleness.js +1 -1
- package/dist/core/graph/graph.js +3 -0
- package/dist/core/graph/import-cycles.d.ts +194 -3
- package/dist/core/graph/import-cycles.js +522 -73
- package/dist/core/graph/types.d.ts +18 -0
- package/dist/core/group/bridge-db.js +5 -27
- package/dist/core/group/cross-impact.d.ts +21 -0
- package/dist/core/group/cross-impact.js +128 -15
- package/dist/core/group/cross-trace.js +27 -16
- package/dist/core/group/extractors/http-patterns/java.js +91 -14
- package/dist/core/group/extractors/http-patterns/node.js +19 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +8 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +1 -0
- package/dist/core/group/extractors/http-route-extractor.js +122 -48
- package/dist/core/group/extractors/include-extractor.js +20 -2
- package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
- package/dist/core/group/extractors/manifest-extractor.js +7 -7
- package/dist/core/group/service.js +10 -1
- package/dist/core/group/storage.js +2 -33
- package/dist/core/group/types.d.ts +18 -0
- package/dist/core/incremental/subgraph-extract.d.ts +9 -6
- package/dist/core/incremental/subgraph-extract.js +33 -10
- package/dist/core/index-freshness.d.ts +80 -0
- package/dist/core/index-freshness.js +120 -0
- package/dist/core/ingestion/call-processor.d.ts +13 -1
- package/dist/core/ingestion/call-processor.js +110 -3
- package/dist/core/ingestion/cfg/callee-cell-format.d.ts +42 -0
- package/dist/core/ingestion/cfg/callee-cell-format.js +42 -0
- package/dist/core/ingestion/cfg/emit.d.ts +8 -23
- package/dist/core/ingestion/cfg/emit.js +9 -23
- package/dist/core/ingestion/class-extractors/configs/go.js +10 -5
- package/dist/core/ingestion/class-extractors/configs/jvm.js +29 -0
- package/dist/core/ingestion/cluster-enricher.js +8 -4
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1 -1
- package/dist/core/ingestion/community-processor.d.ts +21 -3
- package/dist/core/ingestion/community-processor.js +88 -83
- package/dist/core/ingestion/di-extractors/index.d.ts +22 -34
- package/dist/core/ingestion/di-extractors/index.js +17 -18
- package/dist/core/ingestion/di-extractors/spring.d.ts +27 -5
- package/dist/core/ingestion/di-extractors/spring.js +87 -3
- package/dist/core/ingestion/di-extractors/types.d.ts +60 -0
- package/dist/core/ingestion/di-extractors/types.js +15 -0
- package/dist/core/ingestion/emit-references.js +4 -0
- package/dist/core/ingestion/filesystem-walker.js +16 -12
- package/dist/core/ingestion/finalize-orchestrator.js +1 -0
- package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +9 -0
- package/dist/core/ingestion/frameworks/spring/analysis-features.js +41 -0
- package/dist/core/ingestion/frameworks/spring/annotation-arguments.d.ts +13 -0
- package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +227 -0
- package/dist/core/ingestion/frameworks/spring/aop-candidates.d.ts +15 -0
- package/dist/core/ingestion/frameworks/spring/aop-candidates.js +85 -0
- package/dist/core/ingestion/frameworks/spring/aop.d.ts +118 -0
- package/dist/core/ingestion/frameworks/spring/aop.js +476 -0
- package/dist/core/ingestion/frameworks/spring/auto-configuration.d.ts +8 -0
- package/dist/core/ingestion/frameworks/spring/auto-configuration.js +16 -0
- package/dist/core/ingestion/frameworks/spring/bean-candidates.d.ts +30 -0
- package/dist/core/ingestion/frameworks/spring/bean-candidates.js +182 -0
- package/dist/core/ingestion/frameworks/spring/bean-catalog.d.ts +12 -0
- package/dist/core/ingestion/frameworks/spring/bean-catalog.js +27 -0
- package/dist/core/ingestion/frameworks/spring/bean-factories.d.ts +33 -0
- package/dist/core/ingestion/frameworks/spring/bean-factories.js +65 -0
- package/dist/core/ingestion/frameworks/spring/conditionals.d.ts +25 -0
- package/dist/core/ingestion/frameworks/spring/conditionals.js +319 -0
- package/dist/core/ingestion/frameworks/spring/config-bindings.d.ts +24 -0
- package/dist/core/ingestion/frameworks/spring/config-bindings.js +115 -0
- package/dist/core/ingestion/frameworks/spring/di-metadata.d.ts +48 -0
- package/dist/core/ingestion/frameworks/spring/di-metadata.js +379 -0
- package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +28 -0
- package/dist/core/ingestion/frameworks/spring/non-http-handlers.js +152 -0
- package/dist/core/ingestion/frameworks/spring/resource-injection.d.ts +5 -0
- package/dist/core/ingestion/frameworks/spring/resource-injection.js +91 -0
- package/dist/core/ingestion/import-resolvers/configs/csharp.js +5 -1
- package/dist/core/ingestion/import-resolvers/configs/swift.js +13 -0
- package/dist/core/ingestion/import-resolvers/csharp.d.ts +9 -1
- package/dist/core/ingestion/import-resolvers/csharp.js +234 -27
- package/dist/core/ingestion/import-resolvers/go.d.ts +11 -1
- package/dist/core/ingestion/import-resolvers/go.js +44 -17
- package/dist/core/ingestion/import-resolvers/jvm.d.ts +2 -2
- package/dist/core/ingestion/import-resolvers/node-workspace-packages.d.ts +90 -0
- package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +428 -0
- package/dist/core/ingestion/import-resolvers/package-dir-index.d.ts +124 -0
- package/dist/core/ingestion/import-resolvers/package-dir-index.js +223 -0
- package/dist/core/ingestion/import-resolvers/pass-cache.d.ts +50 -0
- package/dist/core/ingestion/import-resolvers/pass-cache.js +60 -0
- package/dist/core/ingestion/import-resolvers/per-file-set.d.ts +71 -0
- package/dist/core/ingestion/import-resolvers/per-file-set.js +11 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/php.js +27 -8
- package/dist/core/ingestion/import-resolvers/python-file-index.d.ts +201 -0
- package/dist/core/ingestion/import-resolvers/python-file-index.js +302 -0
- package/dist/core/ingestion/import-resolvers/python.js +25 -6
- package/dist/core/ingestion/import-resolvers/ruby.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/rust.d.ts +2 -2
- package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/types.d.ts +1 -12
- package/dist/core/ingestion/import-resolvers/utils.d.ts +65 -6
- package/dist/core/ingestion/import-resolvers/utils.js +215 -36
- package/dist/core/ingestion/import-resolvers/workspace-file-index.d.ts +72 -0
- package/dist/core/ingestion/import-resolvers/workspace-file-index.js +58 -0
- package/dist/core/ingestion/language-config.d.ts +20 -1
- package/dist/core/ingestion/language-config.js +18 -23
- package/dist/core/ingestion/language-provider.d.ts +133 -7
- package/dist/core/ingestion/languages/c/captures.js +63 -0
- package/dist/core/ingestion/languages/c/import-target.js +12 -16
- package/dist/core/ingestion/languages/c/query.js +17 -0
- package/dist/core/ingestion/languages/c/scope-resolver.js +26 -19
- package/dist/core/ingestion/languages/c/static-linkage.js +12 -16
- package/dist/core/ingestion/languages/c-cpp.js +13 -0
- package/dist/core/ingestion/languages/cobol/captures.js +93 -0
- package/dist/core/ingestion/languages/cobol/scope-resolver.js +34 -20
- package/dist/core/ingestion/languages/cobol.js +13 -0
- package/dist/core/ingestion/languages/cpp/adl.d.ts +2 -2
- package/dist/core/ingestion/languages/cpp/adl.js +67 -44
- package/dist/core/ingestion/languages/cpp/captures.js +222 -3
- package/dist/core/ingestion/languages/cpp/file-local-linkage.js +11 -15
- package/dist/core/ingestion/languages/cpp/inline-namespaces.d.ts +28 -8
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +242 -68
- package/dist/core/ingestion/languages/cpp/interpret.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/interpret.js +126 -6
- package/dist/core/ingestion/languages/cpp/query.js +106 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +62 -20
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +2 -15
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +18 -10
- package/dist/core/ingestion/languages/csharp/captures.js +44 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +46 -72
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +5 -1
- package/dist/core/ingestion/languages/csharp/query.js +25 -5
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +75 -2
- package/dist/core/ingestion/languages/dart/captures.d.ts +10 -0
- package/dist/core/ingestion/languages/dart/captures.js +686 -16
- package/dist/core/ingestion/languages/dart/extension-type-preprocess.d.ts +7 -0
- package/dist/core/ingestion/languages/dart/extension-type-preprocess.js +30 -0
- package/dist/core/ingestion/languages/dart/import-target.js +32 -9
- package/dist/core/ingestion/languages/dart/index.d.ts +1 -1
- package/dist/core/ingestion/languages/dart/index.js +1 -1
- package/dist/core/ingestion/languages/dart/query.js +98 -1
- package/dist/core/ingestion/languages/dart/scope-resolver.js +14 -3
- package/dist/core/ingestion/languages/dart/simple-hooks.js +9 -0
- package/dist/core/ingestion/languages/dart.js +8 -1
- package/dist/core/ingestion/languages/go/captures.js +142 -3
- package/dist/core/ingestion/languages/go/generic-type-parameters.d.ts +31 -0
- package/dist/core/ingestion/languages/go/generic-type-parameters.js +140 -0
- package/dist/core/ingestion/languages/go/import-target.d.ts +3 -10
- package/dist/core/ingestion/languages/go/import-target.js +34 -30
- package/dist/core/ingestion/languages/go/index.d.ts +1 -1
- package/dist/core/ingestion/languages/go/interface-impls.d.ts +9 -1
- package/dist/core/ingestion/languages/go/interface-impls.js +693 -87
- package/dist/core/ingestion/languages/go/interpret.d.ts +2 -0
- package/dist/core/ingestion/languages/go/interpret.js +22 -8
- package/dist/core/ingestion/languages/go/method-owners.js +31 -12
- package/dist/core/ingestion/languages/go/package-clause.d.ts +46 -0
- package/dist/core/ingestion/languages/go/package-clause.js +75 -0
- package/dist/core/ingestion/languages/go/package-siblings.js +7 -11
- package/dist/core/ingestion/languages/go/query.js +56 -3
- package/dist/core/ingestion/languages/go/scope-resolver.js +39 -0
- package/dist/core/ingestion/languages/go.js +9 -0
- package/dist/core/ingestion/languages/java/analysis-features.d.ts +7 -0
- package/dist/core/ingestion/languages/java/analysis-features.js +25 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +38 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.js +124 -0
- package/dist/core/ingestion/languages/java/captures.d.ts +4 -3
- package/dist/core/ingestion/languages/java/captures.js +321 -49
- package/dist/core/ingestion/languages/java/import-target.d.ts +49 -9
- package/dist/core/ingestion/languages/java/import-target.js +68 -88
- package/dist/core/ingestion/languages/java/interpret.js +3 -2
- package/dist/core/ingestion/languages/java/module-resolution.d.ts +62 -0
- package/dist/core/ingestion/languages/java/module-resolution.js +142 -0
- package/dist/core/ingestion/languages/java/package-facts.d.ts +5 -0
- package/dist/core/ingestion/languages/java/package-facts.js +9 -0
- package/dist/core/ingestion/languages/java/package-siblings.d.ts +3 -16
- package/dist/core/ingestion/languages/java/package-siblings.js +8 -147
- package/dist/core/ingestion/languages/java/query.js +39 -3
- package/dist/core/ingestion/languages/java/record-components.d.ts +14 -0
- package/dist/core/ingestion/languages/java/record-components.js +185 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +35 -2
- package/dist/core/ingestion/languages/java/spring-aop.d.ts +12 -0
- package/dist/core/ingestion/languages/java/spring-aop.js +49 -0
- package/dist/core/ingestion/languages/java/spring-bean-metadata.d.ts +2 -0
- package/dist/core/ingestion/languages/java/spring-bean-metadata.js +8 -0
- package/dist/core/ingestion/languages/java/spring-conditionals.d.ts +11 -0
- package/dist/core/ingestion/languages/java/spring-conditionals.js +48 -0
- package/dist/core/ingestion/languages/java/spring-config-bindings.d.ts +23 -0
- package/dist/core/ingestion/languages/java/spring-config-bindings.js +215 -0
- package/dist/core/ingestion/languages/java/spring-di.d.ts +20 -0
- package/dist/core/ingestion/languages/java/spring-di.js +160 -0
- package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +7 -0
- package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +32 -0
- package/dist/core/ingestion/languages/java.js +76 -3
- package/dist/core/ingestion/languages/javascript/captures.d.ts +6 -1
- package/dist/core/ingestion/languages/javascript/captures.js +278 -4
- package/dist/core/ingestion/languages/javascript/import-target.d.ts +17 -21
- package/dist/core/ingestion/languages/javascript/import-target.js +24 -41
- package/dist/core/ingestion/languages/javascript/index.d.ts +54 -5
- package/dist/core/ingestion/languages/javascript/index.js +54 -5
- package/dist/core/ingestion/languages/javascript/query.d.ts +1 -0
- package/dist/core/ingestion/languages/javascript/query.js +232 -5
- package/dist/core/ingestion/languages/javascript/scope-resolver.js +12 -0
- package/dist/core/ingestion/languages/javascript/simple-hooks.js +2 -1
- package/dist/core/ingestion/languages/jvm/package-facts.d.ts +35 -0
- package/dist/core/ingestion/languages/jvm/package-facts.js +40 -0
- package/dist/core/ingestion/languages/jvm/package-siblings.d.ts +14 -0
- package/dist/core/ingestion/languages/jvm/package-siblings.js +197 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +40 -3
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +111 -8
- package/dist/core/ingestion/languages/kotlin/captures.js +112 -0
- package/dist/core/ingestion/languages/kotlin/import-target.d.ts +8 -1
- package/dist/core/ingestion/languages/kotlin/import-target.js +20 -123
- package/dist/core/ingestion/languages/kotlin/index.d.ts +1 -0
- package/dist/core/ingestion/languages/kotlin/index.js +1 -0
- package/dist/core/ingestion/languages/kotlin/module-resolution.d.ts +22 -0
- package/dist/core/ingestion/languages/kotlin/module-resolution.js +108 -0
- package/dist/core/ingestion/languages/kotlin/package-facts.d.ts +5 -0
- package/dist/core/ingestion/languages/kotlin/package-facts.js +9 -0
- package/dist/core/ingestion/languages/kotlin/package-siblings.d.ts +4 -0
- package/dist/core/ingestion/languages/kotlin/package-siblings.js +8 -0
- package/dist/core/ingestion/languages/kotlin/query.js +47 -2
- package/dist/core/ingestion/languages/kotlin/scope-resolver.js +36 -3
- package/dist/core/ingestion/languages/kotlin/spring-aop.d.ts +12 -0
- package/dist/core/ingestion/languages/kotlin/spring-aop.js +64 -0
- package/dist/core/ingestion/languages/kotlin/spring-bean-metadata.d.ts +2 -0
- package/dist/core/ingestion/languages/kotlin/spring-bean-metadata.js +8 -0
- package/dist/core/ingestion/languages/kotlin/spring-conditionals.d.ts +12 -0
- package/dist/core/ingestion/languages/kotlin/spring-conditionals.js +49 -0
- package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +20 -0
- package/dist/core/ingestion/languages/kotlin/spring-di.js +299 -0
- package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +10 -0
- package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +41 -0
- package/dist/core/ingestion/languages/kotlin.js +5 -6
- package/dist/core/ingestion/languages/php/captures.d.ts +5 -0
- package/dist/core/ingestion/languages/php/captures.js +359 -51
- package/dist/core/ingestion/languages/php/import-decomposer.js +12 -0
- package/dist/core/ingestion/languages/php/import-target.d.ts +2 -1
- package/dist/core/ingestion/languages/php/import-target.js +229 -7
- package/dist/core/ingestion/languages/php/interpret.js +10 -0
- package/dist/core/ingestion/languages/php/query.js +31 -0
- package/dist/core/ingestion/languages/php/scope-resolver.js +1 -1
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +1 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +1 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +3 -4
- package/dist/core/ingestion/languages/python/captures.js +53 -5
- package/dist/core/ingestion/languages/python/import-decomposer.js +22 -1
- package/dist/core/ingestion/languages/python/import-target.d.ts +42 -1
- package/dist/core/ingestion/languages/python/import-target.js +277 -91
- package/dist/core/ingestion/languages/python/index.d.ts +1 -1
- package/dist/core/ingestion/languages/python/index.js +1 -1
- package/dist/core/ingestion/languages/python/interpret.d.ts +52 -0
- package/dist/core/ingestion/languages/python/interpret.js +249 -3
- package/dist/core/ingestion/languages/python/query.js +9 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +17 -2
- package/dist/core/ingestion/languages/python/receiver-binding.js +192 -2
- package/dist/core/ingestion/languages/python/scope-resolver.js +27 -4
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +5 -5
- package/dist/core/ingestion/languages/python/simple-hooks.js +9 -5
- package/dist/core/ingestion/languages/ruby/captures.js +200 -0
- package/dist/core/ingestion/languages/ruby/import-target.js +7 -5
- package/dist/core/ingestion/languages/ruby/query.js +93 -0
- package/dist/core/ingestion/languages/ruby/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/ruby/simple-hooks.d.ts +1 -1
- package/dist/core/ingestion/languages/ruby/simple-hooks.js +22 -1
- package/dist/core/ingestion/languages/rust/captures.js +52 -1
- package/dist/core/ingestion/languages/rust/interpret.js +18 -0
- package/dist/core/ingestion/languages/rust/method-owners.js +11 -1
- package/dist/core/ingestion/languages/rust/module-path.d.ts +126 -0
- package/dist/core/ingestion/languages/rust/module-path.js +214 -0
- package/dist/core/ingestion/languages/rust/qualified-call.d.ts +41 -0
- package/dist/core/ingestion/languages/rust/qualified-call.js +481 -0
- package/dist/core/ingestion/languages/rust/query.js +48 -6
- package/dist/core/ingestion/languages/rust/range-binding.d.ts +1 -11
- package/dist/core/ingestion/languages/rust/range-binding.js +149 -63
- package/dist/core/ingestion/languages/rust/scope-resolver.js +21 -2
- package/dist/core/ingestion/languages/rust.js +17 -0
- package/dist/core/ingestion/languages/swift/captures.js +99 -3
- package/dist/core/ingestion/languages/swift/conditional-directive-preprocess.d.ts +48 -0
- package/dist/core/ingestion/languages/swift/conditional-directive-preprocess.js +253 -0
- package/dist/core/ingestion/languages/swift/import-target.js +4 -9
- package/dist/core/ingestion/languages/swift/query.js +17 -0
- package/dist/core/ingestion/languages/swift/receiver-binding.d.ts +8 -0
- package/dist/core/ingestion/languages/swift/receiver-binding.js +6 -2
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +90 -1
- package/dist/core/ingestion/languages/typescript/captures.js +288 -5
- package/dist/core/ingestion/languages/typescript/cjs-export-assignment.d.ts +134 -0
- package/dist/core/ingestion/languages/typescript/cjs-export-assignment.js +523 -0
- package/dist/core/ingestion/languages/typescript/cjs-module-exports.d.ts +54 -0
- package/dist/core/ingestion/languages/typescript/cjs-module-exports.js +191 -0
- package/dist/core/ingestion/languages/typescript/file-candidates.d.ts +29 -0
- package/dist/core/ingestion/languages/typescript/file-candidates.js +70 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +28 -1
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +125 -10
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +25 -37
- package/dist/core/ingestion/languages/typescript/import-target.js +24 -26
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +21 -1
- package/dist/core/ingestion/languages/typescript/interpret.js +45 -5
- package/dist/core/ingestion/languages/typescript/module-resolution.d.ts +48 -0
- package/dist/core/ingestion/languages/typescript/module-resolution.js +171 -0
- package/dist/core/ingestion/languages/typescript/nuxt-auto-imports.js +2 -1
- package/dist/core/ingestion/languages/typescript/query.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript/query.js +340 -14
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +27 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +27 -40
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +0 -10
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +22 -22
- package/dist/core/ingestion/languages/typescript/tsconfig.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/tsconfig.js +291 -0
- package/dist/core/ingestion/languages/typescript.js +87 -1
- package/dist/core/ingestion/languages/vue/import-target.d.ts +6 -22
- package/dist/core/ingestion/languages/vue/import-target.js +11 -47
- package/dist/core/ingestion/languages/vue/scope-resolver.js +4 -2
- package/dist/core/ingestion/local-symbol-pruner.js +11 -11
- package/dist/core/ingestion/method-extractors/configs/csharp.js +1 -0
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +16 -0
- package/dist/core/ingestion/parsing-processor.js +32 -1
- package/dist/core/ingestion/pipeline-phases/di.d.ts +8 -33
- package/dist/core/ingestion/pipeline-phases/di.js +275 -157
- package/dist/core/ingestion/pipeline-phases/index.d.ts +3 -0
- package/dist/core/ingestion/pipeline-phases/index.js +3 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +158 -3
- package/dist/core/ingestion/pipeline-phases/parse.js +5 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +11 -2
- package/dist/core/ingestion/pipeline-phases/processes.js +102 -7
- package/dist/core/ingestion/pipeline-phases/routes.js +56 -10
- package/dist/core/ingestion/pipeline-phases/spring-aop.d.ts +34 -0
- package/dist/core/ingestion/pipeline-phases/spring-aop.js +439 -0
- package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +34 -0
- package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +255 -0
- package/dist/core/ingestion/pipeline-phases/spring-config.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/spring-config.js +433 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +7 -0
- package/dist/core/ingestion/pipeline.d.ts +27 -3
- package/dist/core/ingestion/pipeline.js +62 -12
- package/dist/core/ingestion/process-processor.d.ts +109 -2
- package/dist/core/ingestion/process-processor.js +464 -52
- package/dist/core/ingestion/resolve-references.d.ts +6 -1
- package/dist/core/ingestion/resolve-references.js +78 -6
- package/dist/core/ingestion/route-extractors/data-route-table.d.ts +25 -0
- package/dist/core/ingestion/route-extractors/data-route-table.js +864 -0
- package/dist/core/ingestion/route-extractors/dispatch-guard.d.ts +96 -0
- package/dist/core/ingestion/route-extractors/dispatch-guard.js +1184 -0
- package/dist/core/ingestion/route-extractors/spring-shared.d.ts +11 -0
- package/dist/core/ingestion/route-extractors/spring-shared.js +89 -0
- package/dist/core/ingestion/route-extractors/spring.js +109 -24
- package/dist/core/ingestion/scope-extractor-bridge.js +9 -1
- package/dist/core/ingestion/scope-extractor.d.ts +22 -7
- package/dist/core/ingestion/scope-extractor.js +617 -59
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +381 -22
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +16 -5
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +7 -7
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +8 -7
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +15 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +53 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +20 -15
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +275 -41
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +123 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +171 -17
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +56 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +130 -32
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +30 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +46 -3
- package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +64 -0
- package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +1233 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +107 -5
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +590 -33
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +19 -4
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +160 -4
- package/dist/core/ingestion/scope-resolution/passes/imported-value-refs.d.ts +34 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-value-refs.js +62 -0
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +64 -0
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +118 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +109 -8
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +931 -92
- package/dist/core/ingestion/scope-resolution/passes/return-shape-members.d.ts +58 -0
- package/dist/core/ingestion/scope-resolution/passes/return-shape-members.js +458 -0
- package/dist/core/ingestion/scope-resolution/passes/unique-name-properties.d.ts +180 -0
- package/dist/core/ingestion/scope-resolution/passes/unique-name-properties.js +418 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +34 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +82 -38
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +61 -1
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +384 -48
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +112 -2
- package/dist/core/ingestion/scope-resolution/resolution-outcome.js +38 -1
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +25 -3
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +36 -16
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +210 -9
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +968 -56
- package/dist/core/ingestion/scope-resolution/summary-maps.d.ts +28 -0
- package/dist/core/ingestion/scope-resolution/summary-maps.js +50 -0
- package/dist/core/ingestion/scope-resolution/undecided-satisfaction.d.ts +55 -0
- package/dist/core/ingestion/scope-resolution/undecided-satisfaction.js +37 -0
- package/dist/core/ingestion/scope-resolution/unresolved-receivers.d.ts +109 -0
- package/dist/core/ingestion/scope-resolution/unresolved-receivers.js +204 -0
- package/dist/core/ingestion/scope-resolution/utils/definition-id.d.ts +13 -0
- package/dist/core/ingestion/scope-resolution/utils/definition-id.js +19 -0
- package/dist/core/ingestion/scope-resolution/utils/generic-instantiation.d.ts +171 -0
- package/dist/core/ingestion/scope-resolution/utils/generic-instantiation.js +218 -0
- package/dist/core/ingestion/scope-resolution/workspace-index-types.d.ts +34 -0
- package/dist/core/ingestion/scope-resolution/workspace-index-types.js +15 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +7 -20
- package/dist/core/ingestion/taint/typescript-model.js +34 -2
- package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
- package/dist/core/ingestion/tree-sitter-queries.js +688 -5
- package/dist/core/ingestion/type-env.js +11 -7
- package/dist/core/ingestion/type-extractors/jvm.js +17 -2
- package/dist/core/ingestion/type-extractors/shared.d.ts +17 -0
- package/dist/core/ingestion/type-extractors/shared.js +16 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/typescript.js +26 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +311 -14
- package/dist/core/ingestion/utils/ast-helpers.js +745 -48
- package/dist/core/ingestion/utils/call-analysis.d.ts +27 -22
- package/dist/core/ingestion/utils/call-analysis.js +202 -7
- package/dist/core/ingestion/utils/callable-flow-captures.d.ts +153 -0
- package/dist/core/ingestion/utils/callable-flow-captures.js +928 -0
- package/dist/core/ingestion/utils/callable-labels.d.ts +25 -0
- package/dist/core/ingestion/utils/callable-labels.js +31 -0
- package/dist/core/ingestion/utils/effective-ram.d.ts +40 -0
- package/dist/core/ingestion/utils/effective-ram.js +58 -0
- package/dist/core/ingestion/utils/line-base.d.ts +14 -0
- package/dist/core/ingestion/utils/line-base.js +14 -0
- package/dist/core/ingestion/utils/method-props.d.ts +27 -4
- package/dist/core/ingestion/utils/method-props.js +29 -4
- package/dist/core/ingestion/utils/receiver-chain-captures.d.ts +31 -0
- package/dist/core/ingestion/utils/receiver-chain-captures.js +57 -0
- package/dist/core/ingestion/utils/receiver-chain-codec.d.ts +89 -0
- package/dist/core/ingestion/utils/receiver-chain-codec.js +182 -0
- package/dist/core/ingestion/utils/scope-tree-walk.d.ts +22 -0
- package/dist/core/ingestion/utils/scope-tree-walk.js +33 -0
- package/dist/core/ingestion/utils/template-arguments.d.ts +96 -0
- package/dist/core/ingestion/utils/template-arguments.js +196 -0
- package/dist/core/ingestion/utils/type-parameters.d.ts +49 -0
- package/dist/core/ingestion/utils/type-parameters.js +206 -0
- package/dist/core/ingestion/workers/callable-id.d.ts +72 -0
- package/dist/core/ingestion/workers/callable-id.js +116 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +14 -1
- package/dist/core/ingestion/workers/parse-worker.js +409 -39
- package/dist/core/ingestion/workers/worker-pool.d.ts +34 -27
- package/dist/core/ingestion/workers/worker-pool.js +133 -25
- package/dist/core/lbug/csv-generator.d.ts +27 -0
- package/dist/core/lbug/csv-generator.js +80 -17
- package/dist/core/lbug/extension-load-error.d.ts +2 -2
- package/dist/core/lbug/extension-load-error.js +44 -27
- package/dist/core/lbug/extension-loader.d.ts +29 -0
- package/dist/core/lbug/extension-loader.js +32 -7
- package/dist/core/lbug/graph-emit-sink.d.ts +264 -0
- package/dist/core/lbug/graph-emit-sink.js +581 -0
- package/dist/core/lbug/lbug-adapter.d.ts +337 -3
- package/dist/core/lbug/lbug-adapter.js +772 -67
- package/dist/core/lbug/lbug-config.d.ts +93 -6
- package/dist/core/lbug/lbug-config.js +269 -9
- package/dist/core/lbug/native-check.d.ts +39 -0
- package/dist/core/lbug/native-check.js +323 -46
- package/dist/core/lbug/pdg-emit-sink.d.ts +13 -4
- package/dist/core/lbug/pdg-emit-sink.js +21 -111
- package/dist/core/lbug/pool-adapter.d.ts +23 -1
- package/dist/core/lbug/pool-adapter.js +115 -7
- package/dist/core/lbug/query-batch.d.ts +75 -0
- package/dist/core/lbug/query-batch.js +102 -0
- package/dist/core/lbug/rel-pair-routing.d.ts +160 -2
- package/dist/core/lbug/rel-pair-routing.js +228 -10
- package/dist/core/lbug/schema.d.ts +162 -3
- package/dist/core/lbug/schema.js +392 -157
- package/dist/core/lbug/sidecar-recovery.d.ts +1 -0
- package/dist/core/lbug/sidecar-recovery.js +1 -1
- package/dist/core/lbug/sync-csv-writer.d.ts +47 -0
- package/dist/core/lbug/sync-csv-writer.js +108 -0
- package/dist/core/lbug/wal-checkpoint-driver.js +3 -0
- package/dist/core/platform/capabilities.d.ts +0 -1
- package/dist/core/platform/capabilities.js +12 -7
- package/dist/core/run-analyze.d.ts +150 -3
- package/dist/core/run-analyze.js +1562 -151
- package/dist/core/search/bm25-index.d.ts +11 -0
- package/dist/core/search/bm25-index.js +60 -26
- package/dist/core/search/fts-indexes.d.ts +167 -2
- package/dist/core/search/fts-indexes.js +264 -10
- package/dist/core/wiki/graph-queries.js +145 -112
- package/dist/core/wiki/llm-client.d.ts +37 -5
- package/dist/core/wiki/llm-client.js +88 -17
- package/dist/core/wiki/prompts.d.ts +19 -0
- package/dist/core/wiki/prompts.js +20 -1
- package/dist/lib/utils.d.ts +113 -0
- package/dist/lib/utils.js +185 -0
- package/dist/mcp/http-transport.d.ts +5 -0
- package/dist/mcp/http-transport.js +19 -5
- package/dist/mcp/local/aop-metadata.d.ts +77 -0
- package/dist/mcp/local/aop-metadata.js +227 -0
- package/dist/mcp/local/bean-metadata.d.ts +3 -0
- package/dist/mcp/local/bean-metadata.js +46 -0
- package/dist/mcp/local/local-backend.d.ts +167 -6
- package/dist/mcp/local/local-backend.js +1756 -405
- package/dist/mcp/local/path-predicate.d.ts +19 -0
- package/dist/mcp/local/path-predicate.js +21 -0
- package/dist/mcp/local/pdg-impact.d.ts +132 -10
- package/dist/mcp/local/pdg-impact.js +391 -98
- package/dist/mcp/output-budget.d.ts +5 -0
- package/dist/mcp/output-budget.js +52 -0
- package/dist/mcp/read-only-policy.d.ts +12 -0
- package/dist/mcp/read-only-policy.js +102 -0
- package/dist/mcp/repository-policy.d.ts +35 -0
- package/dist/mcp/repository-policy.js +333 -0
- package/dist/mcp/resources.js +34 -7
- package/dist/mcp/server.d.ts +5 -2
- package/dist/mcp/server.js +48 -18
- package/dist/mcp/tools.d.ts +8 -8
- package/dist/mcp/tools.js +91 -20
- package/dist/server/analyze-job.d.ts +40 -2
- package/dist/server/analyze-job.js +30 -2
- package/dist/server/analyze-launch.d.ts +1 -1
- package/dist/server/analyze-launch.js +79 -10
- package/dist/server/analyze-worker-core.d.ts +3 -2
- package/dist/server/analyze-worker-core.js +15 -3
- package/dist/server/analyze-worker-ipc.d.ts +1 -1
- package/dist/server/analyze-worker-ipc.js +4 -0
- package/dist/server/analyze-worker-protocol.d.ts +59 -0
- package/dist/server/analyze-worker-protocol.js +1 -0
- package/dist/server/analyze-worker.d.ts +1 -17
- package/dist/server/analyze-worker.js +29 -6
- package/dist/server/api.d.ts +2 -11
- package/dist/server/api.js +200 -155
- package/dist/server/embed-run-outcome.d.ts +89 -0
- package/dist/server/embed-run-outcome.js +88 -0
- package/dist/server/git-clone.js +6 -2
- package/dist/server/mcp-http.d.ts +1 -1
- package/dist/server/mcp-http.js +4 -2
- package/dist/server/middleware.d.ts +116 -9
- package/dist/server/middleware.js +291 -22
- package/dist/server/sse-progress.d.ts +41 -0
- package/dist/server/sse-progress.js +125 -0
- package/dist/server/upload-ingest.js +2 -1
- package/dist/server/validation.js +2 -1
- package/dist/storage/branch-index.d.ts +11 -6
- package/dist/storage/branch-index.js +15 -7
- package/dist/storage/file-hash.js +2 -2
- package/dist/storage/fs-atomic.d.ts +25 -0
- package/dist/storage/fs-atomic.js +42 -0
- package/dist/storage/git.d.ts +165 -0
- package/dist/storage/git.js +352 -3
- package/dist/storage/index-lock.d.ts +83 -0
- package/dist/storage/index-lock.js +654 -0
- package/dist/storage/parse-cache.js +482 -1
- package/dist/storage/parsedfile-store.d.ts +8 -0
- package/dist/storage/parsedfile-store.js +235 -1
- package/dist/storage/repo-manager.d.ts +29 -309
- package/dist/storage/repo-manager.js +187 -165
- package/dist/storage/repo-meta.d.ts +530 -0
- package/dist/storage/repo-meta.js +100 -0
- package/dist/types/pipeline.d.ts +37 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +1 -1
- package/hooks/claude/gitnexus-hook.cjs +1 -1
- package/hooks/claude/resolve-analyze-cmd.cjs +80 -13
- package/package.json +20 -12
- package/scripts/cross-platform-shard.ts +143 -0
- package/scripts/cross-platform-tests.ts +94 -0
- package/scripts/ensure-fts.ts +14 -3
- package/scripts/run-cross-platform.ts +36 -10
- package/scripts/sync-plugin-manifests.mjs +192 -0
- package/skills/gitnexus-cli.md +3 -3
- package/skills/gitnexus-debugging.md +37 -7
- package/skills/gitnexus-exploring.md +26 -4
- package/skills/gitnexus-impact-analysis.md +71 -10
- package/skills/gitnexus-lfg/README.md +55 -0
- package/skills/gitnexus-lfg/SKILL.md +86 -0
- package/skills/gitnexus-plan/README.md +147 -0
- package/skills/gitnexus-plan/SKILL.md +348 -0
- package/skills/gitnexus-plan/references/context-ledger.md +137 -0
- package/skills/gitnexus-plan/references/context-pack.md +126 -0
- package/skills/gitnexus-plan/references/evidence-provenance.md +309 -0
- package/skills/gitnexus-plan/references/pdg-slice.md +109 -0
- package/skills/gitnexus-plan/references/plan-template.md +201 -0
- package/skills/gitnexus-plan/scripts/evidence-provenance.mjs +2366 -0
- package/skills/gitnexus-refactoring.md +48 -5
- package/skills/gitnexus-review/SKILL.md +279 -0
- package/skills/gitnexus-review/ci-personas/ci-adversarial-lens.md +42 -0
- package/skills/gitnexus-review/ci-personas/ci-blast-radius-lens.md +39 -0
- package/skills/gitnexus-review/ci-personas/ci-correctness-lens.md +37 -0
- package/skills/gitnexus-review/ci-personas/ci-coverage-lens.md +40 -0
- package/skills/gitnexus-review/ci-personas/ci-critic-lens.md +42 -0
- package/skills/gitnexus-review/ci-personas/ci-security-lens.md +39 -0
- package/skills/gitnexus-taint-analysis.md +13 -7
- package/skills/gitnexus-work/README.md +71 -0
- package/skills/gitnexus-work/SKILL.md +272 -0
- package/skills/gitnexus-work/references/evidence-provenance.md +309 -0
- package/skills/gitnexus-work/scripts/evidence-provenance.mjs +2366 -0
- package/vendor/leiden/utils.cjs +28 -1
- package/web/assets/ProcessFlowModal-Badh2Pbg.js +24 -0
- package/web/assets/__vite-browser-external-bMKyYQkO.js +1 -0
- package/web/assets/abnfDiagram-QNN2D4NM-Cq2djWWe.js +1 -0
- package/web/assets/agent-DIzEQ1JJ.js +626 -0
- package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +1 -0
- package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +36 -0
- package/web/assets/blockDiagram-54WLDVWM-Dg_hrRWE.js +132 -0
- package/web/assets/{c4Diagram-Y2BXMSZH-DOXVs7-C.js → c4Diagram-X2LASM7V-CnhoT4aV.js} +6 -6
- package/web/assets/chunk-224SPVON-B8gxtXpy.js +1 -0
- package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +127 -0
- package/web/assets/chunk-2SREHG4O-Dip42xOq.js +1 -0
- package/web/assets/chunk-4RFN2BYJ-DHHWuC-n.js +1 -0
- package/web/assets/chunk-66DQ2XMT-CM0xjFAN.js +1 -0
- package/web/assets/chunk-6BELYETK-ChsYtfHW.js +1 -0
- package/web/assets/chunk-6L755F7B-CMhLBxi2.js +1 -0
- package/web/assets/chunk-7CWYLC5S-DRgGkkpF.js +2 -0
- package/web/assets/chunk-A7VWPJGB-B4O3oQdQ.js +1 -0
- package/web/assets/chunk-BI6VK774-D0oaWk8V.js +1 -0
- package/web/assets/chunk-D6VWDJW2-BxcBdZ9c.js +2 -0
- package/web/assets/chunk-DMV4VAQV-Bn6WXDSX.js +4 -0
- package/web/assets/chunk-E5QJAATJ-dh-5CtIV.js +2 -0
- package/web/assets/chunk-EQFTRU2I-1ix7TzW9.js +2 -0
- package/web/assets/chunk-FWYVLQTC-BgDItRzJ.js +1 -0
- package/web/assets/chunk-HNC4WDU7-DoU3KnA7.js +1 -0
- package/web/assets/chunk-L5GCOVLC-CE2w8Nm_.js +161 -0
- package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +321 -0
- package/web/assets/chunk-LORZLZWG-B57uGw2N.js +88 -0
- package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +62 -0
- package/web/assets/chunk-NV3KIAZN-5yvcpMiu.js +1 -0
- package/web/assets/chunk-OIYT25JQ-CQ6KOZGX.js +1 -0
- package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +10 -0
- package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +156 -0
- package/web/assets/chunk-RRFMTAIC-CMN0wcYA.js +1 -0
- package/web/assets/{chunk-RLI5ZMPA-CkXZ0y0U.js → chunk-SATU7PGQ-B1zhgOvc.js} +1 -1
- package/web/assets/chunk-STOV2HOB-GnExfzIX.js +1 -0
- package/web/assets/chunk-SZD42YQK-ijyKKVr1.js +70 -0
- package/web/assets/chunk-TFPGFQPA-Dj6a-0pC.js +231 -0
- package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +2 -0
- package/web/assets/chunk-TZUXEDM2-B7p9AcTw.js +1 -0
- package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +1 -0
- package/web/assets/{chunk-6764PJDD-C7xxk063.js → chunk-VE5CLXGZ-C1vB7wpY.js} +1 -1
- package/web/assets/chunk-VJFVEZTB-CX66F2ep.js +206 -0
- package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +1 -0
- package/web/assets/chunk-WJBAP47W-BckNkuEN.js +1 -0
- package/web/assets/chunk-XNMVGMAZ-Cg_LnPtj.js +1 -0
- package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +1 -0
- package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +1 -0
- package/web/assets/{cose-bilkent-UX7MHV2Q-BmnZ7Mvj.js → cose-bilkent-RZA4XIPR-BHpK4Crp.js} +1 -1
- package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +1 -0
- package/web/assets/cynefinDiagram-TR7LZUCG-C3Iar9lZ.js +62 -0
- package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +4 -0
- package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +30 -0
- package/web/assets/diagram-JCD7PQ7C-DC5-9FRr.js +24 -0
- package/web/assets/diagram-KFBZAVDW-Dfmf_4ti.js +3 -0
- package/web/assets/diagram-L7DVE3PN-GIRyVUtt.js +41 -0
- package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +24 -0
- package/web/assets/ebnfDiagram-DRHRQJWO-D8_CI4ZF.js +1 -0
- package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +85 -0
- package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +1 -0
- package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +1 -0
- package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +292 -0
- package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +1 -0
- package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +106 -0
- package/web/assets/{index-B4eB4dNZ.js → index-BBNl0qfX.js} +51 -51
- package/web/assets/index-CJDpyH-n.css +2 -0
- package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +1 -0
- package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-BLjivjXL.js → ishikawaDiagram-4JDSWWDM-DmqWtjkl.js} +6 -6
- package/web/assets/{journeyDiagram-M6C3CM3L-Cetgs3NH.js → journeyDiagram-QUS3FAKT-XAzVzrQW.js} +6 -6
- package/web/assets/{kanban-definition-75IXJCU3-DMlCaIpf.js → kanban-definition-D4L7SQKK-YurRnmYn.js} +27 -27
- package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +257 -0
- package/web/assets/{mindmap-definition-2TDM6QVE-B4GbXQBc.js → mindmap-definition-66QP5TD6-ChrWWnil.js} +29 -29
- package/web/assets/node-Ct_zna8U.js +8 -0
- package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +1 -0
- package/web/assets/pegDiagram-YKIO5C6U-QGeQLEq2.js +1 -0
- package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +1 -0
- package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +39 -0
- package/web/assets/preload-helper-Czpn1I53.js +1 -0
- package/web/assets/{quadrantDiagram-VICAPDV7-B1TQnlm0.js → quadrantDiagram-VO5QLCXA-B7098wSc.js} +6 -6
- package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +1 -0
- package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +1 -0
- package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +1 -0
- package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +1 -0
- package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +1 -0
- package/web/assets/railroadDiagram-M77LTMSB-AXu4vxk_.js +1 -0
- package/web/assets/requirementDiagram-RASO5HTO-B-l8InpK.js +84 -0
- package/web/assets/sankeyDiagram-F32LOJ6L-Kd7zjJgX.js +40 -0
- package/web/assets/sdk-CzHHH8tD.js +37 -0
- package/web/assets/sequenceDiagram-URATNSBD-BlcdY9DS.js +162 -0
- package/web/assets/sizeCapture-5GORAGP4-BEqQzsxs.js +1 -0
- package/web/assets/src-C5sx_gMp.js +1 -0
- package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +1 -0
- package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +1 -0
- package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +2 -0
- package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +8 -0
- package/web/assets/timeline-definition-WJXWRH4I-DUfp1R3E.js +120 -0
- package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +1 -0
- package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +1 -0
- package/web/assets/vennDiagram-K3VCMQLI-Bj-FN-XS.js +34 -0
- package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +1 -0
- package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +78 -0
- package/web/assets/xychartDiagram-5VRUVLCW-CK_rbSWE.js +7 -0
- package/web/index.html +17 -15
- package/dist/core/ingestion/languages/python/index-stats.d.ts +0 -18
- package/dist/core/ingestion/languages/python/index-stats.js +0 -25
- package/skills/gitnexus-pr-review.md +0 -163
- package/web/assets/ProcessFlowModal-Dna7GkwO.js +0 -22
- package/web/assets/agent-DNYDy-KA.js +0 -660
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BVWFvYz2.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-BU5ZRITg.js +0 -36
- package/web/assets/blockDiagram-7IZFK4PR-Dut1BpTV.js +0 -132
- package/web/assets/chunk-2T2R6R2M-pkw2oXHH.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CXlhF3Pt.js +0 -1
- package/web/assets/chunk-3SSMPTDK-rPsRM1x0.js +0 -321
- package/web/assets/chunk-4R4BOZG6-Be_F0JoX.js +0 -159
- package/web/assets/chunk-67TQ5CYL-zxhDWgyF.js +0 -124
- package/web/assets/chunk-7J6CGLKN-AmkN8jJN.js +0 -10
- package/web/assets/chunk-C62D2QBJ-3gdL8EKc.js +0 -1
- package/web/assets/chunk-CEXFNPSA-CjwtMDkc.js +0 -1
- package/web/assets/chunk-INKRHTLW-D9BM9HWZ.js +0 -70
- package/web/assets/chunk-J5EP6P6S-L19oz55R.js +0 -1
- package/web/assets/chunk-KGFNY3KK-tD2F6wxK.js +0 -62
- package/web/assets/chunk-KGYTTC2M-zm6n242I.js +0 -1
- package/web/assets/chunk-KNLZD3CH-BGlP6X9R.js +0 -1
- package/web/assets/chunk-LCXTWHL2-Ba9JK-6F.js +0 -231
- package/web/assets/chunk-LII3EMHJ-EGx-eBIu.js +0 -1
- package/web/assets/chunk-LRIF4GLE-C3_PqUBq.js +0 -1
- package/web/assets/chunk-QA3QBVWF-D4cSJBf6.js +0 -2
- package/web/assets/chunk-RERM46MO-B5dAdWpP.js +0 -1
- package/web/assets/chunk-RG4AUYOV-OqBBMwuQ.js +0 -206
- package/web/assets/chunk-RKZBBQEN-MyIQbGgD.js +0 -1
- package/web/assets/chunk-T2UQINTJ-BJusEiGe.js +0 -1
- package/web/assets/chunk-UP6H54XL-BG-n7qDp.js +0 -1
- package/web/assets/chunk-UXSXWOXI-CW1_r5cS.js +0 -1
- package/web/assets/chunk-UY5QBCOK-BCpUlARQ.js +0 -1
- package/web/assets/chunk-VU6ZFW4Y-BeXVk45_.js +0 -2
- package/web/assets/chunk-ZXARS5L4-BI_qR2Nm.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-D9zSIg0x.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-D9zSIg0x.js +0 -1
- package/web/assets/dagre-ND4H6XIP-CpcV5kbl.js +0 -4
- package/web/assets/diagram-3NCE3AQN-6SMxBRpj.js +0 -43
- package/web/assets/diagram-GF46GFSD-BZEdNcRZ.js +0 -24
- package/web/assets/diagram-HNR7UZ2L-DxKW2a2Y.js +0 -3
- package/web/assets/diagram-QXG6HAR7-HfTHcXX_.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-CyijUS8r.js +0 -10
- package/web/assets/erDiagram-L5TCEMPS-D0fvpWEX.js +0 -85
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-CgFBM7e-.js +0 -1
- package/web/assets/flowDiagram-H6V6AXG4-Bvr9bbhk.js +0 -162
- package/web/assets/ganttDiagram-JCBTUEKG-TzODrLzv.js +0 -292
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DCJv7SII.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-ONQSwJIY.js +0 -106
- package/web/assets/index-CX_fADmQ.css +0 -2
- package/web/assets/info-J43DQDTF-KCYPFFUO-BfNy-9Nd.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DIPVUIQ6.js +0 -2
- package/web/assets/katex-K3KEBU37-qRQBy-EZ.js +0 -261
- package/web/assets/packet-YPE3B663-LP52Z2RK-D4qkeG9k.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-B_yMKyQa.js +0 -1
- package/web/assets/pieDiagram-CU6KROY3-D3YC_xyg.js +0 -30
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DeD6yN_0.js +0 -1
- package/web/assets/requirementDiagram-JXO7QTGE-f8wczLQf.js +0 -84
- package/web/assets/sankeyDiagram-URQDO5SZ-BTxzG5j0.js +0 -40
- package/web/assets/sequenceDiagram-VS2MUI6T-DuamM-IR.js +0 -162
- package/web/assets/src-BNCXICdA.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-Ch2pEUcw.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-7ANuoZLL.js +0 -1
- package/web/assets/timeline-definition-O6YCAMPW-07BR8R8G.js +0 -120
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CDuSYnd4.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-imazucyJ.js +0 -1
- package/web/assets/vennDiagram-MWXL3ELB-BQX2ossk.js +0 -34
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-DqrT9tB8.js +0 -1
- package/web/assets/wardleyDiagram-CUQ6CDDI-CGIJBBiK.js +0 -78
- package/web/assets/xychartDiagram-N2JHSOCM-B3HNMuWp.js +0 -7
- /package/web/assets/{chunk-7W6UQGC5-pyKGssV3.js → chunk-LIEV3EAG-pyKGssV3.js} +0 -0
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
-
import { createReadStream, createWriteStream, constants as fsConstants } from 'fs';
|
|
2
|
+
import { createReadStream, createWriteStream, existsSync, constants as fsConstants } from 'fs';
|
|
3
3
|
import { createInterface } from 'readline';
|
|
4
4
|
import { once } from 'events';
|
|
5
5
|
import { finished } from 'stream/promises';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import lbug from '@ladybugdb/core';
|
|
8
8
|
import { closeQueryResults } from './query-result-utils.js';
|
|
9
|
+
import { chunk } from '../../lib/utils.js';
|
|
10
|
+
import { warnIfQueryTextUnbounded } from './query-batch.js';
|
|
9
11
|
import { escapeCypherString } from './cypher-escape.js';
|
|
10
12
|
import { withConnLock } from './conn-lock.js';
|
|
11
13
|
import { isWalDriverActive } from './wal-driver-state.js';
|
|
12
14
|
import { NODE_TABLES, REL_TABLE_NAME, SCHEMA_QUERIES, EMBEDDING_TABLE_NAME, CREATE_VECTOR_INDEX_QUERY, STALE_HASH_SENTINEL, } from './schema.js';
|
|
15
|
+
// Analyze-only, but reached from MCP startup via `pool-adapter.js`. #2802
|
|
16
|
+
// proposed lazy-importing it; rejected — `core/search/bm25-index.ts` statically
|
|
17
|
+
// imports `normalizeFtsText` from `csv-generator.js`, and `local-backend.ts`
|
|
18
|
+
// dynamically imports bm25-index on the FTS query path, so deferring here
|
|
19
|
+
// relocates the startup cost to first query rather than removing it. The
|
|
20
|
+
// measured figures live in #2802; they were environment-bound, this is not.
|
|
13
21
|
import { streamAllCSVsToDisk } from './csv-generator.js';
|
|
22
|
+
import { PDG_EDGE_TYPES } from './pdg-emit-sink.js';
|
|
14
23
|
import { getNodeLabel as deriveNodeLabel } from './rel-pair-routing.js';
|
|
15
24
|
import { EMBEDDABLE_LABELS } from '../embeddings/types.js';
|
|
16
|
-
import { extensionManager } from './extension-loader.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import {
|
|
25
|
+
import { extensionManager, getFtsCapability, resolveAnalyzeInstallPolicy, } from './extension-loader.js';
|
|
26
|
+
// Remedy classification for LOAD failures (#2374/#2383). Pure + node:fs only, so
|
|
27
|
+
// this adds no cycle: `extension-loader.ts` already depends on it.
|
|
28
|
+
import { diagnoseExtensionLoad } from './extension-load-error.js';
|
|
29
|
+
import { classifyDeleteAllError, closeLbugConnection, HANDLE_RELEASE_PROBE_ATTEMPTS, HANDLE_RELEASE_PROBE_DELAY_MS, isDbBusyError, isOpenRetryExhausted, isWalCorruptionError, bufferPoolExhaustionRemedy, openLbugConnection, sleep, toNativeSafePath, resolveNativeSafeStorageDir, WAL_RECOVERY_SUGGESTION, waitForWindowsHandleRelease, } from './lbug-config.js';
|
|
30
|
+
import { cleanQuarantinedMissingShadowWals, finalizeLbugSidecarsAfterClose, guardWalQuarantine, isMissingShadowSidecarError, isReadOnlyShadowReplayError, lbugLockRemediation, preflightLbugSidecars, quarantineWalForMissingShadow, renameFailureMessage, shadowSidecarRecoveryMessage, sidecarPreflightDisabled, } from './sidecar-recovery.js';
|
|
20
31
|
import { logger } from '../logger.js';
|
|
32
|
+
import { SPRING_AUTO_CONFIGURATION_REASONS, SPRING_AUTO_CONFIGURATION_SYNTHETIC_ID_PREFIX, } from '../ingestion/frameworks/spring/auto-configuration.js';
|
|
33
|
+
import { SPRING_AOP_EVIDENCE_ID_PREFIX } from '../ingestion/frameworks/spring/aop.js';
|
|
21
34
|
/**
|
|
22
35
|
* Split a relationship CSV into per-label-pair files on disk.
|
|
23
36
|
*
|
|
@@ -399,6 +412,12 @@ const readQueryRows = async (queryResult) => {
|
|
|
399
412
|
throw firstError;
|
|
400
413
|
return rows;
|
|
401
414
|
};
|
|
415
|
+
// Deliberately NOT covered by `warnIfQueryTextUnbounded` (#2915): this is the
|
|
416
|
+
// write/DDL raw path, and `batchInsertNodesToLbug` inlines a node's `content`
|
|
417
|
+
// here, so any source file over the 64 KB text ceiling would trip the heuristic
|
|
418
|
+
// on a query that is entirely legitimate. The guard sits on the read entry
|
|
419
|
+
// points (`executePrepared`, `streamQuery`), which is where a caller-sized list
|
|
420
|
+
// gets spliced into query TEXT.
|
|
402
421
|
const queryAndDrain = async (targetConn, cypher) => {
|
|
403
422
|
const run = async () => {
|
|
404
423
|
const queryResult = await targetConn.query(cypher);
|
|
@@ -409,6 +428,9 @@ const queryAndDrain = async (targetConn, cypher) => {
|
|
|
409
428
|
// connections skip the lock — see isSharedSingletonConn.
|
|
410
429
|
return isSharedSingletonConn(targetConn) ? withConnLock(run) : run();
|
|
411
430
|
};
|
|
431
|
+
// determinism: probe — existence only. Every call site runs this through
|
|
432
|
+
// `queryAndDrain`, which drains and discards the rows; the ONLY observable is
|
|
433
|
+
// whether the read-only shadow replay throws, so no row identity is read.
|
|
412
434
|
const READ_ONLY_SHADOW_REPLAY_PROBE = 'MATCH (n) RETURN n LIMIT 1';
|
|
413
435
|
/**
|
|
414
436
|
* Serve-side entry to the shared WAL-quarantine safety gate. Refuses (throws)
|
|
@@ -666,7 +688,8 @@ const doInitLbug = async (dbPath, readOnly = false) => {
|
|
|
666
688
|
const realPath = await fs.realpath(dbPath);
|
|
667
689
|
const parentDir = path.dirname(dbPath);
|
|
668
690
|
const realParent = await fs.realpath(parentDir);
|
|
669
|
-
|
|
691
|
+
const safePrefix = realParent.endsWith(path.sep) ? realParent : realParent + path.sep;
|
|
692
|
+
if (!realPath.startsWith(safePrefix) && realPath !== realParent) {
|
|
670
693
|
throw new Error(`Refusing to delete ${dbPath}: resolved path ${realPath} is outside storage directory`);
|
|
671
694
|
}
|
|
672
695
|
// Old-style directory database or empty leftover - remove it
|
|
@@ -688,6 +711,26 @@ const doInitLbug = async (dbPath, readOnly = false) => {
|
|
|
688
711
|
// -------------------------------------------------------------------------
|
|
689
712
|
const releaseInitLock = await acquireInitLock(dbPath);
|
|
690
713
|
try {
|
|
714
|
+
// Reclaim missing-shadow WAL quarantines from a PRIOR crash (#2637).
|
|
715
|
+
// LadybugDB renames an unrecoverable WAL aside as
|
|
716
|
+
// `${dbPath}.wal.missing-shadow.<ts>-<rand>` (quarantineWalForMissingShadow)
|
|
717
|
+
// instead of deleting it. Once quarantined it is permanently detached from
|
|
718
|
+
// the live store and never reopened, so reclaiming it is safe regardless of
|
|
719
|
+
// whether the main DB file exists this run — unlike the orphan-sidecar
|
|
720
|
+
// cleanup below, this must NOT be gated on "main DB missing": a quarantine
|
|
721
|
+
// event and a healthy main DB are independent facts. Never let a reclaim
|
|
722
|
+
// failure (e.g. a transient EBUSY from an antivirus scan) block DB startup.
|
|
723
|
+
if (!sidecarPreflightDisabled()) {
|
|
724
|
+
try {
|
|
725
|
+
const reclaimed = await cleanQuarantinedMissingShadowWals(dbPath);
|
|
726
|
+
for (const file of reclaimed) {
|
|
727
|
+
logger.warn(`GitNexus: reclaimed quarantined WAL ${path.basename(file)} from a prior crash`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
catch (err) {
|
|
731
|
+
logger.warn(`GitNexus: failed to reclaim missing-shadow WAL quarantines: ${summarizeError(err)}`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
691
734
|
// Crash-recovery cleanup: if the main DB file is missing, stale sidecars
|
|
692
735
|
// from an interrupted run can block fresh opens indefinitely.
|
|
693
736
|
try {
|
|
@@ -754,7 +797,13 @@ const doInitLbug = async (dbPath, readOnly = false) => {
|
|
|
754
797
|
// FTS powers baseline search, so initialize it with the core DB. Read-only
|
|
755
798
|
// serve/MCP paths must never run DDL or trigger network INSTALL; analyze owns
|
|
756
799
|
// schema/index creation and extension installation.
|
|
757
|
-
|
|
800
|
+
//
|
|
801
|
+
// `quiet` on the writable branch: on a cold machine this pre-load is EXPECTED
|
|
802
|
+
// to miss (default policy is load-only, extension not yet on disk) and analyze
|
|
803
|
+
// Phase 3 installs it moments later in the same run. Warning here reported a
|
|
804
|
+
// degradation that never happened — the run went on to build every FTS index.
|
|
805
|
+
// Phase 3 (and the read-only branch) still warn for real failures.
|
|
806
|
+
await loadFTSExtension(undefined, readOnly ? { policy: 'load-only' } : { quiet: true });
|
|
758
807
|
currentDbPath = dbPath;
|
|
759
808
|
return { db, conn };
|
|
760
809
|
};
|
|
@@ -780,6 +829,21 @@ const copyCsvWithRetry = async (targetConn, copyQuery, onError) => {
|
|
|
780
829
|
}
|
|
781
830
|
}
|
|
782
831
|
};
|
|
832
|
+
/**
|
|
833
|
+
* A staging CSV named in the COPY manifest is gone by the time COPY runs.
|
|
834
|
+
*
|
|
835
|
+
* Only tables with `rows > 0` enter the manifest (see `csv-generator.ts`), so
|
|
836
|
+
* the file WAS written during this run and something removed it since. Raw,
|
|
837
|
+
* that surfaces as a LadybugDB "Binder exception: No file found that matches
|
|
838
|
+
* the pattern …" and then an ENOENT on the next file — two engine-level
|
|
839
|
+
* messages that name neither the cause nor a remedy, and which the field
|
|
840
|
+
* reports show operators hitting on a forced rebuild with nothing to act on.
|
|
841
|
+
*/
|
|
842
|
+
export const missingStagingCsvError = (table, csvPath, rows) => new Error(`Staging CSV for ${table} is missing: ${csvPath}. It was written with ` +
|
|
843
|
+
`${rows.toLocaleString()} rows during this run, so it was removed mid-run — most often a ` +
|
|
844
|
+
`second \`gitnexus analyze\` on the same repo (both use .gitnexus/csv), or an external ` +
|
|
845
|
+
`cleanup of .gitnexus/. Ensure no other analyze is running, then re-run ` +
|
|
846
|
+
`\`gitnexus analyze --force\`.`);
|
|
783
847
|
/**
|
|
784
848
|
* Bulk-COPY every node CSV sequentially on the single writable connection
|
|
785
849
|
* (LadybugDB allows one write txn at a time). Extracted from loadGraphToLbug so
|
|
@@ -788,15 +852,47 @@ const copyCsvWithRetry = async (targetConn, copyQuery, onError) => {
|
|
|
788
852
|
* keeps the IGNORE_ERRORS=true retry; a hard failure throws (no node rows ⇒ the
|
|
789
853
|
* relationship COPY would dangle on missing endpoints).
|
|
790
854
|
*/
|
|
855
|
+
/**
|
|
856
|
+
* Re-check a staging CSV a few times before declaring it gone.
|
|
857
|
+
*
|
|
858
|
+
* Review asked whether turning a silent degrade into a hard abort was
|
|
859
|
+
* deliberate. It is — a fallback that recovers zero rows is exactly the
|
|
860
|
+
* confident-empty failure this work is about, so failing loud is right. But the
|
|
861
|
+
* transient the error message itself names, a second concurrent `analyze`
|
|
862
|
+
* sharing `.gitnexus/csv`, is a RACE, and aborting a multi-minute rebuild on
|
|
863
|
+
* one stat() is a harsh answer to a file that may reappear microseconds later.
|
|
864
|
+
*
|
|
865
|
+
* Bounded and short: three extra looks over ~150ms total. Long enough to ride
|
|
866
|
+
* out a rename or a slow network filesystem, far too short to mask a file that
|
|
867
|
+
* is genuinely gone.
|
|
868
|
+
*/
|
|
869
|
+
const stagingCsvExists = async (csvPath) => {
|
|
870
|
+
const RETRY_DELAYS_MS = [25, 50, 75];
|
|
871
|
+
if (existsSync(csvPath))
|
|
872
|
+
return true;
|
|
873
|
+
for (const delay of RETRY_DELAYS_MS) {
|
|
874
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
875
|
+
if (existsSync(csvPath))
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
return false;
|
|
879
|
+
};
|
|
791
880
|
const copyNodeCSVs = async (targetConn, nodeFileEntries, log, totalSteps) => {
|
|
792
881
|
let stepsDone = 0;
|
|
793
882
|
for (const [table, { csvPath, rows }] of nodeFileEntries) {
|
|
794
883
|
stepsDone++;
|
|
795
884
|
log(`Loading nodes ${stepsDone}/${totalSteps}: ${table} (${rows.toLocaleString()} rows)`);
|
|
885
|
+
if (!(await stagingCsvExists(csvPath)))
|
|
886
|
+
throw missingStagingCsvError(table, csvPath, rows);
|
|
796
887
|
const copyQuery = getCopyQuery(table, normalizeCopyPath(csvPath));
|
|
797
888
|
await copyCsvWithRetry(targetConn, copyQuery, (retryErr) => {
|
|
798
889
|
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
799
|
-
|
|
890
|
+
// Pool exhaustion gets a remedy (#2631): the raw binder text gives the
|
|
891
|
+
// operator nothing to act on, and on non-4K-page hosts (Ascend aarch64,
|
|
892
|
+
// Apple Silicon) the pool bills up to pageSize/4KiB x faster than the
|
|
893
|
+
// sizing was calibrated for — name the knob and the mechanism.
|
|
894
|
+
const remedy = bufferPoolExhaustionRemedy(retryMsg);
|
|
895
|
+
throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}${remedy ? ` ${remedy}` : ''}`);
|
|
800
896
|
});
|
|
801
897
|
}
|
|
802
898
|
};
|
|
@@ -821,7 +917,16 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress,
|
|
|
821
917
|
* was on the in-memory `graph` holds zero BasicBlocks, so `streamAllCSVsToDisk`
|
|
822
918
|
* emits none — the manifest is the sole source and there is no double-COPY.
|
|
823
919
|
*/
|
|
824
|
-
pdgEmitManifest
|
|
920
|
+
pdgEmitManifest,
|
|
921
|
+
/**
|
|
922
|
+
* Streamed structural-emit manifest (#2680). Unlike {@link pdgEmitManifest},
|
|
923
|
+
* these pair keys are NOT disjoint from the whole-graph emit's: a streamed
|
|
924
|
+
* `CALLS` edge is `Function|Function`, exactly like the retained edges
|
|
925
|
+
* `streamAllCSVsToDisk` just wrote. So these files are APPENDED as additional
|
|
926
|
+
* COPY jobs for the same pair rather than merged into `relsByPair` (a Map,
|
|
927
|
+
* which holds one CSV per pair and would silently drop one of them).
|
|
928
|
+
*/
|
|
929
|
+
graphEmitManifest) => {
|
|
825
930
|
if (!conn) {
|
|
826
931
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
827
932
|
}
|
|
@@ -942,21 +1047,42 @@ pdgEmitManifest) => {
|
|
|
942
1047
|
const { relsByPair, relHeader, skippedRels, totalValidRels } = csvResult;
|
|
943
1048
|
let tCopyRels = tCopyNodes;
|
|
944
1049
|
let tFallback = tCopyNodes;
|
|
945
|
-
|
|
1050
|
+
// One COPY job per CSV FILE, not per label pair. The whole-graph emit writes
|
|
1051
|
+
// at most one file per pair, but the streamed structural manifest (#2680) can
|
|
1052
|
+
// contribute a second file for a pair the whole-graph emit also wrote — both
|
|
1053
|
+
// must load. `relsByPair` stays a one-file-per-pair Map so the PDG merge above
|
|
1054
|
+
// and every other consumer are untouched.
|
|
1055
|
+
const copyJobs = [];
|
|
1056
|
+
for (const [pairKey, meta] of relsByPair) {
|
|
1057
|
+
copyJobs.push({ pairKey, csvPath: meta.csvPath, rows: meta.rows });
|
|
1058
|
+
}
|
|
1059
|
+
if (graphEmitManifest) {
|
|
1060
|
+
for (const [pairKey, meta] of graphEmitManifest.relsByPair) {
|
|
1061
|
+
copyJobs.push({ pairKey, csvPath: meta.csvPath, rows: meta.rows });
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
const insertedRels = totalValidRels + (graphEmitManifest?.totalRows ?? 0);
|
|
946
1065
|
const warnings = [];
|
|
1066
|
+
let poolRemedyIssued = false;
|
|
947
1067
|
if (insertedRels > 0) {
|
|
948
|
-
log(`Loading edges: ${insertedRels.toLocaleString()} across ${
|
|
1068
|
+
log(`Loading edges: ${insertedRels.toLocaleString()} across ${copyJobs.length} CSV files`);
|
|
949
1069
|
let pairIdx = 0;
|
|
950
1070
|
let failedPairEdges = 0;
|
|
951
1071
|
const failedPairCsvPaths = new Set();
|
|
952
|
-
for (const
|
|
1072
|
+
for (const { pairKey, csvPath: pairCsvPath, rows } of copyJobs) {
|
|
953
1073
|
pairIdx++;
|
|
954
1074
|
const [fromLabel, toLabel] = pairKey.split('|');
|
|
1075
|
+
// Same guarantee as the node COPY: a pair file only reaches this loop
|
|
1076
|
+
// with rows on it, so an absent file means it vanished mid-run. This is
|
|
1077
|
+
// the `rel_Folder_File.csv` ENOENT the field reports end on.
|
|
1078
|
+
if (!(await stagingCsvExists(pairCsvPath))) {
|
|
1079
|
+
throw missingStagingCsvError(`${fromLabel} -> ${toLabel}`, pairCsvPath, rows);
|
|
1080
|
+
}
|
|
955
1081
|
const normalizedPath = normalizeCopyPath(pairCsvPath);
|
|
956
1082
|
// PARALLEL=false is load-bearing here too — see COPY_CSV_OPTS (#2203 / kuzudb/kuzu#5778).
|
|
957
1083
|
const copyQuery = `COPY ${REL_TABLE_NAME} FROM "${normalizedPath}" (from="${fromLabel}", to="${toLabel}", HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
|
|
958
1084
|
if (pairIdx % 5 === 0 || rows > 1000) {
|
|
959
|
-
log(`Loading edges: ${pairIdx}/${
|
|
1085
|
+
log(`Loading edges: ${pairIdx}/${copyJobs.length} files (${fromLabel} -> ${toLabel})`);
|
|
960
1086
|
}
|
|
961
1087
|
// Use the captured `writeConn` (not the module-level `conn`) for the rel
|
|
962
1088
|
// COPY, matching the node COPY above — one captured reference for the whole
|
|
@@ -966,6 +1092,17 @@ pdgEmitManifest) => {
|
|
|
966
1092
|
await copyCsvWithRetry(writeConn, copyQuery, (retryErr) => {
|
|
967
1093
|
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
968
1094
|
warnings.push(`${fromLabel}->${toLabel} (${rows} edges): ${retryMsg.slice(0, 80)}`);
|
|
1095
|
+
// One remedy per bulk load, not per pair (#2631): pool exhaustion
|
|
1096
|
+
// repeats for every remaining pair once it starts. logger.warn, not
|
|
1097
|
+
// just warnings.push — the returned warnings array has no consumer at
|
|
1098
|
+
// any call site, so a push alone would leave the remedy invisible
|
|
1099
|
+
// while the row-by-row fallback quietly degrades the load.
|
|
1100
|
+
const remedy = poolRemedyIssued ? undefined : bufferPoolExhaustionRemedy(retryMsg);
|
|
1101
|
+
if (remedy) {
|
|
1102
|
+
poolRemedyIssued = true;
|
|
1103
|
+
warnings.push(remedy);
|
|
1104
|
+
logger.warn(remedy);
|
|
1105
|
+
}
|
|
969
1106
|
failedPairEdges += rows;
|
|
970
1107
|
failedPairCsvPaths.add(pairCsvPath);
|
|
971
1108
|
});
|
|
@@ -1106,6 +1243,12 @@ const formatCypherValue = (v) => {
|
|
|
1106
1243
|
return String(v);
|
|
1107
1244
|
return `'${escapeCypherString(String(v))}'`;
|
|
1108
1245
|
};
|
|
1246
|
+
const formatCypherStringArray = (value) => {
|
|
1247
|
+
const items = Array.isArray(value)
|
|
1248
|
+
? value.filter((item) => typeof item === 'string')
|
|
1249
|
+
: [];
|
|
1250
|
+
return `[${items.map(formatCypherValue).join(', ')}]`;
|
|
1251
|
+
};
|
|
1109
1252
|
/**
|
|
1110
1253
|
* Fallback: insert relationships one-by-one if COPY fails.
|
|
1111
1254
|
*
|
|
@@ -1187,11 +1330,14 @@ export const getCopyQuery = (table, filePath) => {
|
|
|
1187
1330
|
// `calleeIds` is its SOUND parallel (space-joined resolved callee ids, #2227).
|
|
1188
1331
|
return `COPY ${t}(id, filePath, startLine, endLine, text, callees, calleeIds) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1189
1332
|
}
|
|
1333
|
+
if (table === 'Class') {
|
|
1334
|
+
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, frameworkAnnotations) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1335
|
+
}
|
|
1190
1336
|
if (table === 'Method') {
|
|
1191
1337
|
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1192
1338
|
}
|
|
1193
1339
|
if (table === 'Property') {
|
|
1194
|
-
return `COPY ${t}(id, name, filePath, startLine, endLine, content, description, declaredType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1340
|
+
return `COPY ${t}(id, name, filePath, startLine, endLine, content, description, declaredType, isDetail) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1195
1341
|
}
|
|
1196
1342
|
// TypeScript/JS code element tables have isExported; multi-language tables do not
|
|
1197
1343
|
if (TABLES_WITH_EXPORTED.has(table)) {
|
|
@@ -1236,6 +1382,12 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
1236
1382
|
// is the sound resolved-id parallel to the leaf-name `callees` set.
|
|
1237
1383
|
query = `CREATE (n:BasicBlock {id: ${formatCypherValue(properties.id)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, text: ${formatCypherValue(properties.text || '')}, callees: ${formatCypherValue(properties.callees || '')}, calleeIds: ${formatCypherValue(properties.calleeIds || '')}})`;
|
|
1238
1384
|
}
|
|
1385
|
+
else if (label === 'Class') {
|
|
1386
|
+
const descPart = properties.description
|
|
1387
|
+
? `, description: ${formatCypherValue(properties.description)}`
|
|
1388
|
+
: '';
|
|
1389
|
+
query = `CREATE (n:Class {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${formatCypherValue(properties.content || '')}${descPart}, frameworkAnnotations: ${formatCypherStringArray(properties.frameworkAnnotations)}})`;
|
|
1390
|
+
}
|
|
1239
1391
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1240
1392
|
const descPart = properties.description
|
|
1241
1393
|
? `, description: ${formatCypherValue(properties.description)}`
|
|
@@ -1246,7 +1398,7 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
1246
1398
|
const descPart = properties.description
|
|
1247
1399
|
? `, description: ${formatCypherValue(properties.description)}`
|
|
1248
1400
|
: '';
|
|
1249
|
-
query = `CREATE (n:${t} {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${formatCypherValue(properties.content || '')}${descPart}, declaredType: ${formatCypherValue(properties.declaredType || '')}})`;
|
|
1401
|
+
query = `CREATE (n:${t} {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${formatCypherValue(properties.content || '')}${descPart}, declaredType: ${formatCypherValue(properties.declaredType || '')}, isDetail: ${properties.isDetail === true}})`;
|
|
1250
1402
|
}
|
|
1251
1403
|
else {
|
|
1252
1404
|
// Multi-language tables (Struct, Impl, Trait, Macro, etc.) — no isExported
|
|
@@ -1320,6 +1472,12 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
1320
1472
|
// (#2227) is the sound resolved-id parallel to the `callees` set.
|
|
1321
1473
|
query = `MERGE (n:BasicBlock {id: ${formatCypherValue(properties.id)}}) SET n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.text = ${formatCypherValue(properties.text || '')}, n.callees = ${formatCypherValue(properties.callees || '')}, n.calleeIds = ${formatCypherValue(properties.calleeIds || '')}`;
|
|
1322
1474
|
}
|
|
1475
|
+
else if (label === 'Class') {
|
|
1476
|
+
const descPart = properties.description
|
|
1477
|
+
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
1478
|
+
: '';
|
|
1479
|
+
query = `MERGE (n:Class {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.frameworkAnnotations = ${formatCypherStringArray(properties.frameworkAnnotations)}`;
|
|
1480
|
+
}
|
|
1323
1481
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1324
1482
|
const descPart = properties.description
|
|
1325
1483
|
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
@@ -1330,7 +1488,7 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
1330
1488
|
const descPart = properties.description
|
|
1331
1489
|
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
1332
1490
|
: '';
|
|
1333
|
-
query = `MERGE (n:${t} {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.declaredType = ${formatCypherValue(properties.declaredType || '')}`;
|
|
1491
|
+
query = `MERGE (n:${t} {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.declaredType = ${formatCypherValue(properties.declaredType || '')}, n.isDetail = ${properties.isDetail === true}`;
|
|
1334
1492
|
}
|
|
1335
1493
|
else {
|
|
1336
1494
|
const descPart = properties.description
|
|
@@ -1352,10 +1510,15 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
1352
1510
|
}
|
|
1353
1511
|
return { inserted, failed };
|
|
1354
1512
|
};
|
|
1513
|
+
// Guarded by `executePrepared` — a pure delegation, so warning here too would
|
|
1514
|
+
// double-report the same query text (#2915).
|
|
1355
1515
|
export const executeQuery = async (cypher) => {
|
|
1356
1516
|
return await executePrepared(cypher, {});
|
|
1357
1517
|
};
|
|
1358
1518
|
export const streamQuery = async (cypher, onRow) => {
|
|
1519
|
+
// The other raw `conn.query` read entry point (`executePrepared` covers the
|
|
1520
|
+
// prepared path, and `executeQuery` delegates to it). Never throws (#2915).
|
|
1521
|
+
warnIfQueryTextUnbounded(cypher, 'streamQuery', (message) => logger.warn(message));
|
|
1359
1522
|
if (isWalDriverActive()) {
|
|
1360
1523
|
// streamQuery reads rows on the singleton connection WITHOUT withConnLock; if
|
|
1361
1524
|
// the WAL-checkpoint driver is live, those reads could race a CHECKPOINT — the
|
|
@@ -1401,6 +1564,8 @@ export const streamQuery = async (cypher, onRow) => {
|
|
|
1401
1564
|
* Prevents Cypher injection by binding values as parameters.
|
|
1402
1565
|
*/
|
|
1403
1566
|
export const executePrepared = async (cypher, params) => {
|
|
1567
|
+
// A `.length` compare on text we already hold; never throws (#2915).
|
|
1568
|
+
warnIfQueryTextUnbounded(cypher, 'executePrepared', (message) => logger.warn(message));
|
|
1404
1569
|
const c = conn;
|
|
1405
1570
|
if (!c) {
|
|
1406
1571
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
@@ -1423,8 +1588,8 @@ export const executeWithReusedStatement = async (cypher, paramsList) => {
|
|
|
1423
1588
|
if (paramsList.length === 0)
|
|
1424
1589
|
return;
|
|
1425
1590
|
const SUB_BATCH_SIZE = 4;
|
|
1426
|
-
for (
|
|
1427
|
-
const
|
|
1591
|
+
for (const [subBatchIndex, subBatch] of chunk(paramsList, SUB_BATCH_SIZE).entries()) {
|
|
1592
|
+
const firstRow = subBatchIndex * SUB_BATCH_SIZE;
|
|
1428
1593
|
// One critical section per sub-batch: the prepare + its executes run with
|
|
1429
1594
|
// exclusive access to the connection (so the WAL checkpoint driver cannot
|
|
1430
1595
|
// interleave a CHECKPOINT mid-batch), while the lock is released between
|
|
@@ -1443,16 +1608,33 @@ export const executeWithReusedStatement = async (cypher, paramsList) => {
|
|
|
1443
1608
|
catch (e) {
|
|
1444
1609
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1445
1610
|
const queryPreview = cypher.replace(/\s+/g, ' ').slice(0, 120);
|
|
1446
|
-
throw new Error(`Batch execution failed for rows ${
|
|
1611
|
+
throw new Error(`Batch execution failed for rows ${firstRow + 1}-${firstRow + subBatch.length}: ${msg} (${queryPreview})`);
|
|
1447
1612
|
}
|
|
1448
1613
|
// Note: LadybugDB PreparedStatement doesn't require explicit close()
|
|
1449
1614
|
});
|
|
1450
1615
|
}
|
|
1451
1616
|
};
|
|
1617
|
+
/**
|
|
1618
|
+
* Node and edge totals for the open index.
|
|
1619
|
+
*
|
|
1620
|
+
* `edges` is `undefined` when the count could NOT BE TAKEN, and that is a
|
|
1621
|
+
* different fact from zero. It used to be initialised to 0 with the query in a
|
|
1622
|
+
* swallowing `catch`, so a WAL/lock contention throw during finalize — a
|
|
1623
|
+
* documented hazard on this exact call — returned a measured-looking 0. The
|
|
1624
|
+
* collapse check downstream then read a perfectly healthy index as a total
|
|
1625
|
+
* write collapse, which is precisely the confident-zero failure that check
|
|
1626
|
+
* exists to prevent.
|
|
1627
|
+
*/
|
|
1452
1628
|
export const getLbugStats = async () => {
|
|
1453
1629
|
const c = conn;
|
|
1454
|
-
if (!c)
|
|
1455
|
-
return {
|
|
1630
|
+
if (!c) {
|
|
1631
|
+
return {
|
|
1632
|
+
nodes: 0,
|
|
1633
|
+
edges: undefined,
|
|
1634
|
+
structuralEdges: undefined,
|
|
1635
|
+
structuralEdgesError: 'no open connection',
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1456
1638
|
// Called during analyze finalize while the WAL-checkpoint driver is still
|
|
1457
1639
|
// running; each count read takes the connection lock so it cannot execute
|
|
1458
1640
|
// concurrently with a driver CHECKPOINT. Per-query locking lets the driver
|
|
@@ -1470,7 +1652,7 @@ export const getLbugStats = async () => {
|
|
|
1470
1652
|
// ignore
|
|
1471
1653
|
}
|
|
1472
1654
|
}
|
|
1473
|
-
let totalEdges
|
|
1655
|
+
let totalEdges;
|
|
1474
1656
|
try {
|
|
1475
1657
|
totalEdges = await withConnLock(async () => {
|
|
1476
1658
|
const queryResult = await c.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`);
|
|
@@ -1479,9 +1661,34 @@ export const getLbugStats = async () => {
|
|
|
1479
1661
|
});
|
|
1480
1662
|
}
|
|
1481
1663
|
catch {
|
|
1482
|
-
//
|
|
1664
|
+
// Leave `totalEdges` undefined: the count was not obtained. Reporting 0
|
|
1665
|
+
// here is what made a throwing query indistinguishable from an empty table.
|
|
1666
|
+
}
|
|
1667
|
+
// Structural-only count for the collapse check. `TAINT_PATH` is deliberately
|
|
1668
|
+
// NOT in `PDG_EDGE_TYPES` — it is a whole-program Function→Function edge that
|
|
1669
|
+
// lives in the in-memory graph and is persisted by the normal emit, so it IS
|
|
1670
|
+
// structural and must stay counted on both sides.
|
|
1671
|
+
let structuralEdges;
|
|
1672
|
+
let structuralEdgesError;
|
|
1673
|
+
try {
|
|
1674
|
+
const excluded = [...PDG_EDGE_TYPES].map((t) => `'${t}'`).join(', ');
|
|
1675
|
+
structuralEdges = await withConnLock(async () => {
|
|
1676
|
+
const queryResult = await c.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() WHERE NOT r.type IN [${excluded}] RETURN count(r) AS cnt`);
|
|
1677
|
+
const rows = await readQueryRows(queryResult);
|
|
1678
|
+
return rows.length > 0 ? Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0) : 0;
|
|
1679
|
+
});
|
|
1483
1680
|
}
|
|
1484
|
-
|
|
1681
|
+
catch (err) {
|
|
1682
|
+
// Same contract as `edges`: leave undefined rather than report a zero the
|
|
1683
|
+
// collapse check would read as a total wipeout. But NOT silent — the reason
|
|
1684
|
+
// travels back on the result and is logged by the caller, so "the guard
|
|
1685
|
+
// declined because it could not measure" is visible instead of looking
|
|
1686
|
+
// identical to "the guard ran and found nothing wrong".
|
|
1687
|
+
structuralEdgesError = err instanceof Error ? err.message : String(err);
|
|
1688
|
+
logger.warn({ err }, 'Structural relationship count failed; the graph-write-collapse check will have no ' +
|
|
1689
|
+
'structural measurement from this run.');
|
|
1690
|
+
}
|
|
1691
|
+
return { nodes: totalNodes, edges: totalEdges, structuralEdges, structuralEdgesError };
|
|
1485
1692
|
};
|
|
1486
1693
|
/**
|
|
1487
1694
|
* Load cached embeddings from LadybugDB before a rebuild.
|
|
@@ -1508,6 +1715,9 @@ export const loadCachedEmbeddings = async () => {
|
|
|
1508
1715
|
// Old schema only had (nodeId, embedding); new schema adds (id, chunkIndex, startLine, endLine, contentHash).
|
|
1509
1716
|
// If the query fails (column missing), we return empty cache to force a full rebuild.
|
|
1510
1717
|
try {
|
|
1718
|
+
// determinism: probe — schema probe, not a sample. `readQueryRows` drains
|
|
1719
|
+
// the result and the rows are dropped on the floor; only whether the
|
|
1720
|
+
// new-schema columns parse decides the branch.
|
|
1511
1721
|
const check = await c.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN e.nodeId AS nodeId, e.chunkIndex AS chunkIndex LIMIT 1`);
|
|
1512
1722
|
await readQueryRows(check);
|
|
1513
1723
|
}
|
|
@@ -1884,15 +2094,17 @@ export const wipeLbugDbFiles = async (lbugPath) => {
|
|
|
1884
2094
|
export const isLbugReady = () => conn !== null && db !== null;
|
|
1885
2095
|
/**
|
|
1886
2096
|
* Multi-label alternation over exactly the labels that can own embedding
|
|
1887
|
-
* rows
|
|
1888
|
-
*
|
|
1889
|
-
*
|
|
2097
|
+
* rows: EMBEDDABLE_LABELS plus File, which embedding-pipeline.ts embeds as
|
|
2098
|
+
* the zero-symbol fallback for text-only repositories (#2454). Reserved
|
|
2099
|
+
* keywords are backtick-escaped via {@link escapeTableName}. Probed on
|
|
2100
|
+
* @ladybugdb/core 0.18.0 (this shipping review, FIX 4): the full multi-label
|
|
1890
2101
|
* alternation parses, executes, and deletes exactly the joined rows —
|
|
1891
2102
|
* replacing the unlabeled `MATCH (n)` that scanned EVERY node table per
|
|
1892
2103
|
* chunk (BasicBlock-dominated under `--pdg`) when only embeddable labels
|
|
1893
|
-
* can match an embedding row.
|
|
2104
|
+
* can match an embedding row. Including File is free for code repositories:
|
|
2105
|
+
* they never hold File embedding rows, so the extra label joins nothing.
|
|
1894
2106
|
*/
|
|
1895
|
-
const embeddableLabelMatch = () => EMBEDDABLE_LABELS.map((l) => escapeTableName(l)).join('|');
|
|
2107
|
+
const embeddableLabelMatch = () => ['File', ...EMBEDDABLE_LABELS].map((l) => escapeTableName(l)).join('|');
|
|
1896
2108
|
// LADYBUGDB-CONTRACT: matches @ladybugdb/core ^0.18.0 native binder text,
|
|
1897
2109
|
// probe-recorded: `Binder exception: Table CodeEmbedding does not exist.`
|
|
1898
2110
|
// When bumping LadybugDB, re-validate — `git grep "LADYBUGDB-CONTRACT"`
|
|
@@ -2028,9 +2240,8 @@ export const deleteNodesForFiles = async (filePaths, options = {}) => {
|
|
|
2028
2240
|
}
|
|
2029
2241
|
const targetConn = conn;
|
|
2030
2242
|
let warnedMissingEmbeddingTable = false;
|
|
2031
|
-
for (
|
|
2032
|
-
const
|
|
2033
|
-
const listLiteral = `[${chunk.map((p) => `'${escapeCypherString(p)}'`).join(', ')}]`;
|
|
2243
|
+
for (const [chunkIndex, batch] of chunk(filePaths, DELETE_FILES_CHUNK_SIZE).entries()) {
|
|
2244
|
+
const listLiteral = `[${batch.map((p) => `'${escapeCypherString(p)}'`).join(', ')}]`;
|
|
2034
2245
|
// Embedding rows key on their OWNING NODE's id: generateId builds
|
|
2035
2246
|
// label-first ids — `${label}:${name}` (src/lib/utils.ts) with qualified
|
|
2036
2247
|
// names that embed the file path (e.g. `Function:src/f.ts:fn0:1`) — so
|
|
@@ -2070,7 +2281,7 @@ export const deleteNodesForFiles = async (filePaths, options = {}) => {
|
|
|
2070
2281
|
const tn = escapeTableName(tableName);
|
|
2071
2282
|
await queryAndDrain(targetConn, `MATCH (n:${tn}) WHERE n.filePath IN ${listLiteral} DETACH DELETE n`);
|
|
2072
2283
|
}
|
|
2073
|
-
options.onChunk?.(Math.min(
|
|
2284
|
+
options.onChunk?.(Math.min((chunkIndex + 1) * DELETE_FILES_CHUNK_SIZE, filePaths.length), filePaths.length);
|
|
2074
2285
|
}
|
|
2075
2286
|
};
|
|
2076
2287
|
export const getEmbeddingTableName = () => EMBEDDING_TABLE_NAME;
|
|
@@ -2148,11 +2359,8 @@ export const queryImportersBatch = async (targetFilePaths, options = {}) => {
|
|
|
2148
2359
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2149
2360
|
}
|
|
2150
2361
|
const importers = new Set();
|
|
2151
|
-
for (
|
|
2152
|
-
|
|
2153
|
-
const chunkIndex = i / DELETE_FILES_CHUNK_SIZE;
|
|
2154
|
-
const chunk = targetFilePaths.slice(i, i + DELETE_FILES_CHUNK_SIZE);
|
|
2155
|
-
const listLiteral = `[${chunk.map((p) => `'${escapeCypherString(p)}'`).join(', ')}]`;
|
|
2362
|
+
for (const [chunkIndex, batch] of chunk(targetFilePaths, DELETE_FILES_CHUNK_SIZE).entries()) {
|
|
2363
|
+
const listLiteral = `[${batch.map((p) => `'${escapeCypherString(p)}'`).join(', ')}]`;
|
|
2156
2364
|
const cypher = `
|
|
2157
2365
|
MATCH (a)-[r:${REL_TABLE_NAME}]->(b)
|
|
2158
2366
|
WHERE r.type = 'IMPORTS' AND b.filePath IN ${listLiteral}
|
|
@@ -2176,9 +2384,9 @@ export const queryImportersBatch = async (targetFilePaths, options = {}) => {
|
|
|
2176
2384
|
// it would have surfaced keeps possibly-stale edges this run, and the
|
|
2177
2385
|
// old bare `catch {}` left no trace of that anywhere. pino idiom:
|
|
2178
2386
|
// `err` key — `error` serializes to `{}`.
|
|
2179
|
-
logger.warn({ err }, `Incremental importer BFS: dropped chunk ${chunkIndex} (${
|
|
2387
|
+
logger.warn({ err }, `Incremental importer BFS: dropped chunk ${chunkIndex} (${batch.length} target path(s)) — ` +
|
|
2180
2388
|
'importer expansion degrades for this run; affected importers may keep stale edges until the next full rebuild.');
|
|
2181
|
-
options.onChunkFailure?.(chunkIndex,
|
|
2389
|
+
options.onChunkFailure?.(chunkIndex, batch.length, err);
|
|
2182
2390
|
}
|
|
2183
2391
|
finally {
|
|
2184
2392
|
if (queryResult)
|
|
@@ -2234,9 +2442,9 @@ export const deleteAllCommunitiesAndProcesses = async () => {
|
|
|
2234
2442
|
/**
|
|
2235
2443
|
* Shared mechanics for the delete-all-relationships-of-one-type family
|
|
2236
2444
|
* ({@link deleteAllInterprocTaintPaths}, {@link deleteAllCallSummaries},
|
|
2237
|
-
* {@link deleteAllInjects}
|
|
2238
|
-
*
|
|
2239
|
-
* endpoints are untouched).
|
|
2445
|
+
* {@link deleteAllInjects}, {@link deleteSpringAutoConfigurationDeclarations}):
|
|
2446
|
+
* count the matching CodeRelation rows, then DELETE them (relationship-level —
|
|
2447
|
+
* these are edge types, not node labels, so endpoints are untouched).
|
|
2240
2448
|
*
|
|
2241
2449
|
* count + DELETE run as one critical section on the singleton connection so a
|
|
2242
2450
|
* concurrent WAL-checkpoint cannot corrupt native state mid-delete (#pdg).
|
|
@@ -2244,8 +2452,9 @@ export const deleteAllCommunitiesAndProcesses = async () => {
|
|
|
2244
2452
|
* @param relType the CodeRelation `type` value to delete (e.g. 'INJECTS')
|
|
2245
2453
|
* @param logTag the `[tag]` prefix on the abort error message
|
|
2246
2454
|
* @param duplicateNoun what the abort message says would be duplicated
|
|
2455
|
+
* @param exactReasons optional reason allowlist for a shared relationship type
|
|
2247
2456
|
*/
|
|
2248
|
-
const deleteAllRelationshipsOfType = async (relType, logTag, duplicateNoun) => {
|
|
2457
|
+
const deleteAllRelationshipsOfType = async (relType, logTag, duplicateNoun, exactReasons) => {
|
|
2249
2458
|
const c = conn;
|
|
2250
2459
|
if (!c) {
|
|
2251
2460
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
@@ -2253,13 +2462,19 @@ const deleteAllRelationshipsOfType = async (relType, logTag, duplicateNoun) => {
|
|
|
2253
2462
|
return withConnLock(async () => {
|
|
2254
2463
|
let edgesDeleted = 0;
|
|
2255
2464
|
let countResult;
|
|
2465
|
+
const reasonFilter = exactReasons === undefined || exactReasons.length === 0
|
|
2466
|
+
? ''
|
|
2467
|
+
: ` AND (${exactReasons
|
|
2468
|
+
.map((reason) => `r.reason = '${escapeCypherString(reason)}'`)
|
|
2469
|
+
.join(' OR ')})`;
|
|
2470
|
+
const predicate = `r.type = '${escapeCypherString(relType)}'${reasonFilter}`;
|
|
2256
2471
|
try {
|
|
2257
|
-
countResult = await c.query(`MATCH ()-[r:CodeRelation]->() WHERE
|
|
2472
|
+
countResult = await c.query(`MATCH ()-[r:CodeRelation]->() WHERE ${predicate} RETURN count(r) AS cnt`);
|
|
2258
2473
|
const result = Array.isArray(countResult) ? countResult[0] : countResult;
|
|
2259
2474
|
const rows = await result.getAll();
|
|
2260
2475
|
const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
|
|
2261
2476
|
if (count > 0) {
|
|
2262
|
-
await closeQueryResults(await c.query(`MATCH ()-[r:CodeRelation]->() WHERE
|
|
2477
|
+
await closeQueryResults(await c.query(`MATCH ()-[r:CodeRelation]->() WHERE ${predicate} DELETE r`));
|
|
2263
2478
|
edgesDeleted = count;
|
|
2264
2479
|
}
|
|
2265
2480
|
}
|
|
@@ -2335,6 +2550,93 @@ export const deleteAllCallSummaries = async () => deleteAllRelationshipsOfType('
|
|
|
2335
2550
|
* DELETE on the typed CodeRelation rows — endpoints are untouched.
|
|
2336
2551
|
*/
|
|
2337
2552
|
export const deleteAllInjects = async () => deleteAllRelationshipsOfType('INJECTS', 'di', 'duplicate INJECTS edges');
|
|
2553
|
+
/**
|
|
2554
|
+
* Drop every Spring AOP `ADVISED_BY` relationship before incremental
|
|
2555
|
+
* writeback. Pointcut/annotation resolution is whole-program: adding a type in
|
|
2556
|
+
* a third file can shadow a wildcard annotation import or change a wildcard
|
|
2557
|
+
* execution match between two otherwise unchanged endpoint files.
|
|
2558
|
+
*/
|
|
2559
|
+
export const deleteAllAdvisedBy = async () => deleteAllRelationshipsOfType('ADVISED_BY', 'spring-aop', 'duplicate ADVISED_BY edges');
|
|
2560
|
+
/** Drop all synthetic Spring AOP evidence nodes before incremental writeback. */
|
|
2561
|
+
export const deleteSpringAopEvidenceNodes = async () => {
|
|
2562
|
+
const c = conn;
|
|
2563
|
+
if (!c) {
|
|
2564
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2565
|
+
}
|
|
2566
|
+
return withConnLock(async () => {
|
|
2567
|
+
let countResult;
|
|
2568
|
+
const idPrefix = escapeCypherString(SPRING_AOP_EVIDENCE_ID_PREFIX);
|
|
2569
|
+
const predicate = `n.id STARTS WITH '${idPrefix}'`;
|
|
2570
|
+
try {
|
|
2571
|
+
countResult = await c.query(`MATCH (n:CodeElement) WHERE ${predicate} RETURN count(n) AS cnt`);
|
|
2572
|
+
const result = Array.isArray(countResult) ? countResult[0] : countResult;
|
|
2573
|
+
const rows = await result.getAll();
|
|
2574
|
+
const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
|
|
2575
|
+
if (count > 0) {
|
|
2576
|
+
await closeQueryResults(await c.query(`MATCH (n:CodeElement) WHERE ${predicate} DETACH DELETE n`));
|
|
2577
|
+
}
|
|
2578
|
+
if (countResult)
|
|
2579
|
+
await closeQueryResults(countResult);
|
|
2580
|
+
return { nodesDeleted: count };
|
|
2581
|
+
}
|
|
2582
|
+
catch (err) {
|
|
2583
|
+
if (countResult)
|
|
2584
|
+
await closeQueryResults(countResult);
|
|
2585
|
+
if (classifyDeleteAllError(err) === 'benign-missing-table') {
|
|
2586
|
+
return { nodesDeleted: 0 };
|
|
2587
|
+
}
|
|
2588
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2589
|
+
throw new Error('[spring-aop] failed to clear synthetic evidence before incremental re-write ' +
|
|
2590
|
+
`(${message}) — aborting to avoid stale advice metadata; the next run will full-rebuild`);
|
|
2591
|
+
}
|
|
2592
|
+
});
|
|
2593
|
+
};
|
|
2594
|
+
/**
|
|
2595
|
+
* Drop Spring-owned auto-configuration `DECLARES` relationships before
|
|
2596
|
+
* incremental writeback. `DECLARES` is generic, so exact reason filtering is
|
|
2597
|
+
* required: other metadata systems must retain their own declarations.
|
|
2598
|
+
*/
|
|
2599
|
+
export const deleteSpringAutoConfigurationDeclarations = async () => deleteAllRelationshipsOfType('DECLARES', 'spring-auto-configuration', 'duplicate auto-configuration declarations', SPRING_AUTO_CONFIGURATION_REASONS);
|
|
2600
|
+
/**
|
|
2601
|
+
* Drop synthetic source-unavailable auto-configuration Class nodes before
|
|
2602
|
+
* incremental writeback. The fresh full graph re-emits every still-needed
|
|
2603
|
+
* synthetic node; deleting first also removes placeholders that became stale
|
|
2604
|
+
* when a real source class appeared.
|
|
2605
|
+
*/
|
|
2606
|
+
export const deleteSpringAutoConfigurationSyntheticClasses = async () => {
|
|
2607
|
+
const c = conn;
|
|
2608
|
+
if (!c) {
|
|
2609
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2610
|
+
}
|
|
2611
|
+
return withConnLock(async () => {
|
|
2612
|
+
let countResult;
|
|
2613
|
+
const idPrefix = escapeCypherString(SPRING_AUTO_CONFIGURATION_SYNTHETIC_ID_PREFIX);
|
|
2614
|
+
const predicate = `n.id STARTS WITH '${idPrefix}'`;
|
|
2615
|
+
try {
|
|
2616
|
+
countResult = await c.query(`MATCH (n:Class) WHERE ${predicate} RETURN count(n) AS cnt`);
|
|
2617
|
+
const result = Array.isArray(countResult) ? countResult[0] : countResult;
|
|
2618
|
+
const rows = await result.getAll();
|
|
2619
|
+
const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
|
|
2620
|
+
if (count > 0) {
|
|
2621
|
+
await closeQueryResults(await c.query(`MATCH (n:Class) WHERE ${predicate} DETACH DELETE n`));
|
|
2622
|
+
}
|
|
2623
|
+
if (countResult)
|
|
2624
|
+
await closeQueryResults(countResult);
|
|
2625
|
+
return { nodesDeleted: count };
|
|
2626
|
+
}
|
|
2627
|
+
catch (err) {
|
|
2628
|
+
if (countResult)
|
|
2629
|
+
await closeQueryResults(countResult);
|
|
2630
|
+
if (classifyDeleteAllError(err) === 'benign-missing-table') {
|
|
2631
|
+
return { nodesDeleted: 0 };
|
|
2632
|
+
}
|
|
2633
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2634
|
+
throw new Error('[spring-auto-configuration] failed to clear synthetic Class nodes before ' +
|
|
2635
|
+
`incremental re-write (${message}) — aborting to avoid stale placeholders; ` +
|
|
2636
|
+
'the next run will full-rebuild');
|
|
2637
|
+
}
|
|
2638
|
+
});
|
|
2639
|
+
};
|
|
2338
2640
|
// ============================================================================
|
|
2339
2641
|
// Full-Text Search (FTS) Functions
|
|
2340
2642
|
// ============================================================================
|
|
@@ -2372,16 +2674,16 @@ export const loadVectorExtension = async (targetConn, opts = {}) => {
|
|
|
2372
2674
|
const useModuleState = targetConn === undefined;
|
|
2373
2675
|
if (useModuleState && vectorExtensionLoaded)
|
|
2374
2676
|
return true;
|
|
2375
|
-
//
|
|
2376
|
-
//
|
|
2377
|
-
//
|
|
2378
|
-
//
|
|
2379
|
-
//
|
|
2380
|
-
//
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2677
|
+
// No platform gate. Windows was hard-refused here for years on the strength
|
|
2678
|
+
// of an early-era report that in-process INSTALL VECTOR could SIGSEGV
|
|
2679
|
+
// (#1365) — but the extension server ships win_amd64 VECTOR artifacts for
|
|
2680
|
+
// every 0.18.x extension version (probed live: v0.18.0 and v0.18.1 both
|
|
2681
|
+
// serve a real PE32+ DLL; the pinned 0.18.2 core resolves its extension
|
|
2682
|
+
// directory to 0.18.1, strace-verified), and INSTALL now runs in a spawned
|
|
2683
|
+
// child process (installDuckDbExtensionOutOfProcess), so even a crashing
|
|
2684
|
+
// installer kills only the child and degrades to `false` here. LOAD of a
|
|
2685
|
+
// present extension file is an ordinary in-process load whose failures
|
|
2686
|
+
// surface as catchable errors, exactly like FTS.
|
|
2385
2687
|
const c = targetConn ?? conn;
|
|
2386
2688
|
if (!c) {
|
|
2387
2689
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
@@ -2475,6 +2777,220 @@ export const createVectorIndex = async () => {
|
|
|
2475
2777
|
throw e;
|
|
2476
2778
|
}
|
|
2477
2779
|
};
|
|
2780
|
+
/**
|
|
2781
|
+
* The three field reads every index-catalog consumer needs, each hedging the
|
|
2782
|
+
* named-record form against the positional one exactly once.
|
|
2783
|
+
*
|
|
2784
|
+
* They exist because the hedge used to be inlined at five call sites across two
|
|
2785
|
+
* modules (#2841 review), one of which — the `dropSearchFTSIndexes` sweep in
|
|
2786
|
+
* `core/search/fts-indexes.ts` — carried no LADYBUGDB-CONTRACT marker at all, so
|
|
2787
|
+
* the doc above claimed a grep that could not find it. Exported for that module;
|
|
2788
|
+
* everything version-coupled about the row shape now lives in this one block.
|
|
2789
|
+
*/
|
|
2790
|
+
export const indexRowTable = (row) => row?.table_name ?? row?.[0];
|
|
2791
|
+
export const indexRowName = (row) => row?.index_name ?? row?.[1];
|
|
2792
|
+
export const indexRowType = (row) => row?.index_type ?? row?.[2];
|
|
2793
|
+
/**
|
|
2794
|
+
* Read the index catalog on the writable connection, or `undefined` when it
|
|
2795
|
+
* cannot be read.
|
|
2796
|
+
*
|
|
2797
|
+
* `SHOW_INDEXES` is readable WITHOUT any extension loaded and reports
|
|
2798
|
+
* `extension_loaded` per index, so the extension-gated-DML checks below settle
|
|
2799
|
+
* the common "this DB carries no such index" case with one local read and no
|
|
2800
|
+
* error-string sniffing. It runs through the unprepared `conn.query()` path
|
|
2801
|
+
* like every other `CALL` procedure here (#2114).
|
|
2802
|
+
*
|
|
2803
|
+
* `undefined` means "could not prove anything" and every caller must treat it
|
|
2804
|
+
* as fail-closed (assume an index may be present), never as "no indexes". All
|
|
2805
|
+
* three readers below honour that, `ftsIndexExistsInCatalog` included since
|
|
2806
|
+
* #2841 review H3. To hand ONE read to several gates, use
|
|
2807
|
+
* {@link readIndexCatalogSnapshot} — passing this `undefined` on cannot be
|
|
2808
|
+
* distinguished from passing nothing at all.
|
|
2809
|
+
*/
|
|
2810
|
+
export const readIndexCatalogRows = async () => {
|
|
2811
|
+
const targetConn = conn;
|
|
2812
|
+
if (!targetConn) {
|
|
2813
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2814
|
+
}
|
|
2815
|
+
try {
|
|
2816
|
+
return (await withConnLock(async () => readQueryRows(await targetConn.query('CALL SHOW_INDEXES() RETURN *'))));
|
|
2817
|
+
}
|
|
2818
|
+
catch (err) {
|
|
2819
|
+
logger.warn({ err }, 'Could not read the LadybugDB index catalog (CALL SHOW_INDEXES()); ' +
|
|
2820
|
+
'extension-gated DML checks must assume an index may be present.');
|
|
2821
|
+
return undefined;
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
/**
|
|
2825
|
+
* The failed half of an {@link IndexCatalogSnapshot}: the caller DID read the
|
|
2826
|
+
* catalog and could not prove anything.
|
|
2827
|
+
*
|
|
2828
|
+
* It exists because `undefined` was overloaded (#2841 review §5.A). The gates
|
|
2829
|
+
* below took `indexRows?: IndexCatalogRow[]`, so "my read failed" and "I passed
|
|
2830
|
+
* you nothing" were the SAME value, and each gate's `?? (await
|
|
2831
|
+
* readIndexCatalogRows())` silently re-read the catalog — turning the one shared
|
|
2832
|
+
* read the call site documents into three round-trips and three identical
|
|
2833
|
+
* warnings on the failure path, with the two gates free to decide from DIFFERENT
|
|
2834
|
+
* snapshots. A distinct sentinel makes "read, unreadable" a value the parameter
|
|
2835
|
+
* can carry, so a supplied snapshot is never re-read.
|
|
2836
|
+
*/
|
|
2837
|
+
export const INDEX_CATALOG_UNREADABLE = Symbol('gitnexus:index-catalog-unreadable');
|
|
2838
|
+
/**
|
|
2839
|
+
* {@link readIndexCatalogRows} in snapshot form — what callers should read once
|
|
2840
|
+
* and pass to EVERY extension-gated-DML gate in a run, so the "one shared
|
|
2841
|
+
* `SHOW_INDEXES` read" invariant holds on the failure branch too (#2841 review
|
|
2842
|
+
* §5.A). The `IndexCatalogRow[] | undefined` spelling stays available for
|
|
2843
|
+
* callers that only want the rows.
|
|
2844
|
+
*/
|
|
2845
|
+
export const readIndexCatalogSnapshot = async () => (await readIndexCatalogRows()) ?? INDEX_CATALOG_UNREADABLE;
|
|
2846
|
+
/**
|
|
2847
|
+
* Resolve a gate's optional `indexRows` argument into the rows it must judge,
|
|
2848
|
+
* reading the catalog AT MOST ONCE and ONLY when the caller supplied nothing.
|
|
2849
|
+
*
|
|
2850
|
+
* The `??` is meaningful again (#2841 review §5.A): `undefined` in can now only
|
|
2851
|
+
* mean "no snapshot supplied", because a caller whose own read failed passes
|
|
2852
|
+
* {@link INDEX_CATALOG_UNREADABLE}, which is truthy and short-circuits it.
|
|
2853
|
+
* `undefined` OUT keeps its documented meaning — "could not prove anything",
|
|
2854
|
+
* which every caller of {@link readIndexCatalogRows} treats as fail-closed.
|
|
2855
|
+
*
|
|
2856
|
+
* Exported for the `dropSearchFTSIndexes` sweep in `core/search/fts-indexes.ts`,
|
|
2857
|
+
* which takes the same optional-snapshot parameter and must resolve it by the
|
|
2858
|
+
* same rules — including the "a supplied snapshot is never re-read" half.
|
|
2859
|
+
*/
|
|
2860
|
+
export const resolveGateRows = async (indexRows) => {
|
|
2861
|
+
const snapshot = indexRows ?? (await readIndexCatalogSnapshot());
|
|
2862
|
+
return snapshot === INDEX_CATALOG_UNREADABLE ? undefined : snapshot;
|
|
2863
|
+
};
|
|
2864
|
+
/**
|
|
2865
|
+
* Make DML against {@link EMBEDDING_TABLE_NAME} legal on the writable
|
|
2866
|
+
* connection when it can be, and report whether it is.
|
|
2867
|
+
*
|
|
2868
|
+
* LadybugDB refuses EVERY mutation of a table carrying an HNSW index while
|
|
2869
|
+
* the VECTOR extension is not loaded on that connection: `DELETE` fails with
|
|
2870
|
+
* "Trying to delete from an index on table CodeEmbedding but its extension is
|
|
2871
|
+
* not loaded", `CREATE` with the matching "insert into an index" variant,
|
|
2872
|
+
* `DROP TABLE` is refused while the index references it, and `SET` — even on
|
|
2873
|
+
* a NON-indexed property — segfaults the process outright. Probed against
|
|
2874
|
+
* @ladybugdb/core 0.18.2 (the lockfile-pinned version) and 0.18.0 — every
|
|
2875
|
+
* result identical on both (#2623).
|
|
2876
|
+
*
|
|
2877
|
+
* Dropping the index is NOT an available recovery: `CALL DROP_VECTOR_INDEX`
|
|
2878
|
+
* is itself a VECTOR-extension function and resolves to "Catalog exception:
|
|
2879
|
+
* function DROP_VECTOR_INDEX is not defined" in exactly the state it would
|
|
2880
|
+
* need to rescue. Loading the extension is the only in-place repair, which is
|
|
2881
|
+
* why this returns a verdict instead of attempting a fixup.
|
|
2882
|
+
*
|
|
2883
|
+
* `true` = embedding-row DML is safe: either VECTOR is now loaded, or the
|
|
2884
|
+
* table carries no index to trip over. `false` = genuinely blocked (index
|
|
2885
|
+
* present, extension unloadable); the analyze orchestrator answers that by
|
|
2886
|
+
* escalating to the wipe-and-rebuild write plan instead of failing
|
|
2887
|
+
* mid-writeback.
|
|
2888
|
+
*
|
|
2889
|
+
* Cheap by construction: one {@link readIndexCatalogRows} read settles the
|
|
2890
|
+
* common "this repo never built an embedding index" case without touching the
|
|
2891
|
+
* extension machinery at all, so a VECTOR-less machine is not charged a bounded
|
|
2892
|
+
* INSTALL attempt on every incremental analyze. (That read's own mechanics and
|
|
2893
|
+
* fail-closed contract are documented there, not re-explained here — #2841
|
|
2894
|
+
* review §5.H.)
|
|
2895
|
+
*
|
|
2896
|
+
* @param indexRows An {@link IndexCatalogSnapshot} the caller already read, so
|
|
2897
|
+
* one `SHOW_INDEXES` read can settle every gate in a run. FRESHNESS CONTRACT:
|
|
2898
|
+
* the snapshot must have been taken on THIS connection with nothing in between
|
|
2899
|
+
* that creates or drops an index — the gate's verdict is only as current as the
|
|
2900
|
+
* rows it is handed. Pass {@link INDEX_CATALOG_UNREADABLE} (what
|
|
2901
|
+
* {@link readIndexCatalogSnapshot} returns) when your own read failed; that
|
|
2902
|
+
* fails closed here WITHOUT a second read. Omit the argument entirely to have
|
|
2903
|
+
* the gate read the catalog itself.
|
|
2904
|
+
*/
|
|
2905
|
+
export const ensureEmbeddingRowDmlSafe = async (indexRows) => {
|
|
2906
|
+
// Unconditional precondition (#2841 review §5.B). This check used to run on
|
|
2907
|
+
// every call; adding `indexRows` moved it inside `readIndexCatalogRows`, where
|
|
2908
|
+
// a caller-supplied snapshot skips it — so a closed DB could be answered
|
|
2909
|
+
// `true` where it previously threw. The verdict is only meaningful for the
|
|
2910
|
+
// live writable connection, so assert that before looking at the argument.
|
|
2911
|
+
if (!conn) {
|
|
2912
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2913
|
+
}
|
|
2914
|
+
// Catalog FIRST. The overwhelmingly common case on a repo that never enabled
|
|
2915
|
+
// embeddings is "no index at all", and that is provable with one local read
|
|
2916
|
+
// — no extension needed. Loading first would make every incremental analyze
|
|
2917
|
+
// on a VECTOR-less machine pay a bounded out-of-process INSTALL attempt (the
|
|
2918
|
+
// `auto` policy) plus an "extension unavailable" warning, for a repo that
|
|
2919
|
+
// can never hit this hazard.
|
|
2920
|
+
const rows = await resolveGateRows(indexRows);
|
|
2921
|
+
// Any non-HASH index on the embedding table gates DML. Keyed on index TYPE,
|
|
2922
|
+
// not name, so an index built under a different name still counts; the
|
|
2923
|
+
// implicit primary-key HASH index is engine-internal and never gates.
|
|
2924
|
+
const indexGatesDml = rows === undefined ||
|
|
2925
|
+
rows.some((row) => {
|
|
2926
|
+
if (indexRowTable(row) !== EMBEDDING_TABLE_NAME)
|
|
2927
|
+
return false;
|
|
2928
|
+
return indexRowType(row) !== 'HASH';
|
|
2929
|
+
});
|
|
2930
|
+
if (!indexGatesDml)
|
|
2931
|
+
return true;
|
|
2932
|
+
return await loadVectorExtension(undefined, { policy: resolveAnalyzeInstallPolicy() });
|
|
2933
|
+
};
|
|
2934
|
+
/**
|
|
2935
|
+
* The FTS twin of {@link ensureEmbeddingRowDmlSafe} (#2841).
|
|
2936
|
+
*
|
|
2937
|
+
* LadybugDB refuses DML against a table carrying an FTS index while the FTS
|
|
2938
|
+
* extension is not loaded on that connection, and it refuses it at BIND time —
|
|
2939
|
+
* probed against @ladybugdb/core 0.18.3, a DETACH DELETE matching ZERO rows
|
|
2940
|
+
* fails just as hard as one matching thousands ("Binder exception: Trying to
|
|
2941
|
+
* delete from an index on table File but its extension is not loaded"). Every
|
|
2942
|
+
* table in `FTS_INDEXES` is therefore immutable until the extension loads, and
|
|
2943
|
+
* there is no narrower escape: `CALL DROP_FTS_INDEX` is itself an
|
|
2944
|
+
* FTS-extension function ("Catalog exception: function DROP_FTS_INDEX is not
|
|
2945
|
+
* defined" in exactly the state that would need rescuing), and LadybugDB has
|
|
2946
|
+
* no SQL `DROP INDEX` at all (both spellings are Parser exceptions). Rebuilding
|
|
2947
|
+
* the DB file is the only way to clear the indexes without the extension.
|
|
2948
|
+
*
|
|
2949
|
+
* `true` = FTS-indexed-table DML is safe: either FTS is now loaded, or the DB
|
|
2950
|
+
* carries no FTS index to trip over. `false` = genuinely blocked; the analyze
|
|
2951
|
+
* orchestrator answers that by escalating to the wipe-and-rebuild write plan
|
|
2952
|
+
* instead of dying mid-writeback with an engine error that never says "FTS".
|
|
2953
|
+
*
|
|
2954
|
+
* Catalog-first for the same reason as the VECTOR twin: a repo whose index
|
|
2955
|
+
* never carried FTS must not pay a bounded INSTALL attempt on every
|
|
2956
|
+
* incremental analyze. Keyed on index TYPE, so an index left over from an
|
|
2957
|
+
* older `FTS_INDEXES` (different name/table set) still counts.
|
|
2958
|
+
*
|
|
2959
|
+
* @param indexRows Same contract as {@link ensureEmbeddingRowDmlSafe}'s: an
|
|
2960
|
+
* {@link IndexCatalogSnapshot} read on THIS connection with no index created or
|
|
2961
|
+
* dropped since, so both gates decide from the SAME snapshot and the catalog is
|
|
2962
|
+
* read once per run. {@link INDEX_CATALOG_UNREADABLE} fails closed here without
|
|
2963
|
+
* a second read; omitting the argument makes the gate read for itself.
|
|
2964
|
+
*/
|
|
2965
|
+
export const ensureFtsRowDmlSafe = async (indexRows) => {
|
|
2966
|
+
// Unconditional precondition, same regression as the VECTOR twin's (#2841
|
|
2967
|
+
// review §5.B): a caller-supplied snapshot must not let a closed DB be
|
|
2968
|
+
// answered `true`.
|
|
2969
|
+
if (!conn) {
|
|
2970
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2971
|
+
}
|
|
2972
|
+
const rows = await resolveGateRows(indexRows);
|
|
2973
|
+
// LADYBUGDB-CONTRACT: the `'FTS'` index_type spelling — see {@link IndexCatalogRow}.
|
|
2974
|
+
//
|
|
2975
|
+
// Polarity (#2841 review §6.A): a row whose type cannot be read gates DML.
|
|
2976
|
+
// A bare `=== 'FTS'` answers `undefined === 'FTS'` → false → *no gate*, i.e.
|
|
2977
|
+
// it falls OPEN in the one gate whose only job is preventing an unsafe write,
|
|
2978
|
+
// while the VECTOR twin above falls CLOSED for the same input. Deliberately
|
|
2979
|
+
// NOT expressed as the twin's `!== 'HASH'`: that predicate is safe there only
|
|
2980
|
+
// because it is scoped to `EMBEDDING_TABLE_NAME` first, whereas this gate is
|
|
2981
|
+
// table-agnostic, so `!== 'HASH'` would let the HNSW vector index gate FTS
|
|
2982
|
+
// DML and charge every embeddings-enabled repo an FTS load it does not need.
|
|
2983
|
+
const indexGatesDml = rows === undefined ||
|
|
2984
|
+
rows.some((row) => {
|
|
2985
|
+
const indexType = indexRowType(row);
|
|
2986
|
+
// Unreadable shape ⇒ "might be FTS" ⇒ gate. Only a positively-identified
|
|
2987
|
+
// non-FTS index is waved through.
|
|
2988
|
+
return indexType === undefined || indexType === 'FTS';
|
|
2989
|
+
});
|
|
2990
|
+
if (!indexGatesDml)
|
|
2991
|
+
return true;
|
|
2992
|
+
return await loadFTSExtension(undefined, { policy: resolveAnalyzeInstallPolicy() });
|
|
2993
|
+
};
|
|
2478
2994
|
/**
|
|
2479
2995
|
* Lazy-create an FTS index, caching the fact in-process.
|
|
2480
2996
|
*
|
|
@@ -2510,6 +3026,89 @@ export const ensureFTSIndex = async (tableName, indexName, properties, stemmer =
|
|
|
2510
3026
|
throw e;
|
|
2511
3027
|
}
|
|
2512
3028
|
};
|
|
3029
|
+
/**
|
|
3030
|
+
* Classify a `QUERY_FTS_INDEX` failure so a genuinely-missing index (normal —
|
|
3031
|
+
* this table's FTS index hasn't been built yet) is distinguished from a real
|
|
3032
|
+
* query-time error that would otherwise look identical (#2767), and from the
|
|
3033
|
+
* table itself being missing (schema drift / a corrupted or partial DB — a
|
|
3034
|
+
* much more serious condition than an unbuilt index).
|
|
3035
|
+
*
|
|
3036
|
+
* tri-review Residual-1: this used to be a second, independently-maintained
|
|
3037
|
+
* classifier living in `core/search/bm25-index.ts` (re-exported from there
|
|
3038
|
+
* for backward compatibility), duplicating this function's job for the
|
|
3039
|
+
* IDENTICAL `QUERY_FTS_INDEX` cypher call. `queryFTS` below now uses this
|
|
3040
|
+
* same classifier for its own catch instead of a bare, unanchored
|
|
3041
|
+
* `.includes('does not exist')` check that could not tell "index missing"
|
|
3042
|
+
* from "table missing" apart, and silently swallowed both alike.
|
|
3043
|
+
*
|
|
3044
|
+
* Three real message shapes were confirmed empirically against a live
|
|
3045
|
+
* `CALL QUERY_FTS_INDEX(...)`:
|
|
3046
|
+
* `"Prepare failed: Binder exception: Table <T> doesn't have an index with
|
|
3047
|
+
* name <name>."` — the table exists, only its FTS index is missing (normal,
|
|
3048
|
+
* benign — `missing-index`) — `"Prepare failed: Binder exception: Table <T>
|
|
3049
|
+
* does not exist."` — the TABLE ITSELF is missing (`missing-table`) — and a
|
|
3050
|
+
* `Catalog exception: function QUERY_FTS_INDEX is not defined...` when the
|
|
3051
|
+
* FTS extension isn't loaded at all (`other`; mirrors the confirmed
|
|
3052
|
+
* `DROP_FTS_INDEX` shape in {@link isBenignDropFtsIndexError}'s doc comment).
|
|
3053
|
+
*
|
|
3054
|
+
* Anchored to the exception class (after stripping the optional "Prepare
|
|
3055
|
+
* failed: " wrapper LadybugDB adds for statement-preparation failures),
|
|
3056
|
+
* mirroring `isBenignDropFtsIndexError`'s START-of-message anchor: a bare
|
|
3057
|
+
* substring search would misclassify a genuine, differently-classed error
|
|
3058
|
+
* (e.g. a `Runtime exception` from the FTS parser that echoes the user's
|
|
3059
|
+
* own search text back into its message) as benign whenever that echoed
|
|
3060
|
+
* text happened to contain "does not exist" — silently dropping a real
|
|
3061
|
+
* error, the exact #2767 failure mode this function exists to prevent.
|
|
3062
|
+
*/
|
|
3063
|
+
export const classifyFtsQueryError = (message) => {
|
|
3064
|
+
const PREPARE_FAILED_PREFIX = 'Prepare failed: ';
|
|
3065
|
+
const body = message.startsWith(PREPARE_FAILED_PREFIX)
|
|
3066
|
+
? message.slice(PREPARE_FAILED_PREFIX.length)
|
|
3067
|
+
: message;
|
|
3068
|
+
if (!body.startsWith('Binder exception:') && !body.startsWith('Catalog exception:')) {
|
|
3069
|
+
return 'other';
|
|
3070
|
+
}
|
|
3071
|
+
if (body.includes("doesn't have an index"))
|
|
3072
|
+
return 'missing-index';
|
|
3073
|
+
if (body.includes('does not exist'))
|
|
3074
|
+
return 'missing-table';
|
|
3075
|
+
return 'other';
|
|
3076
|
+
};
|
|
3077
|
+
/**
|
|
3078
|
+
* Build the `QUERY_FTS_INDEX` statement shared by BOTH FTS read paths —
|
|
3079
|
+
* `queryFTS` below and `queryFTSViaExecutor` in `core/search/bm25-index.ts`
|
|
3080
|
+
* (the MCP connection-pool path). The two ran byte-identical cypher from two
|
|
3081
|
+
* places, so every change had to be applied twice in lockstep — the `, node.id`
|
|
3082
|
+
* ORDER BY tiebreak for #2787 being the latest. Lives beside
|
|
3083
|
+
* {@link classifyFtsQueryError}, which was already shared for exactly this call.
|
|
3084
|
+
*/
|
|
3085
|
+
/**
|
|
3086
|
+
* DETAIL SYMBOLS DO NOT COMPETE IN TEXT SEARCH.
|
|
3087
|
+
*
|
|
3088
|
+
* `Property.isDetail` marks the keys of an anonymous literal returned from a
|
|
3089
|
+
* function (R3-4): real symbols, worth walking and worth an impact analysis,
|
|
3090
|
+
* but not concepts a text search should surface on their own. Their names are
|
|
3091
|
+
* ordinary words (`message`, `value`, `timestamp`) and there are many of them,
|
|
3092
|
+
* so without this they consume the FTS call's own LIMIT and push out the
|
|
3093
|
+
* CALLABLES named after the same concept — measured, `query('message')` went
|
|
3094
|
+
* from two processes to none on the mini-repo fixture.
|
|
3095
|
+
*
|
|
3096
|
+
* Filtered HERE rather than after the call, because that is the only place it
|
|
3097
|
+
* works: rows crowded out by the LIMIT never reach the caller, so no amount of
|
|
3098
|
+
* re-ranking downstream can recover them. (Tried, and it recovered nothing.)
|
|
3099
|
+
*
|
|
3100
|
+
* Property-only, since no other table has the column, and `IS NULL`-tolerant so
|
|
3101
|
+
* an index written before this column existed still answers.
|
|
3102
|
+
*/
|
|
3103
|
+
const FTS_DETAIL_FILTER = `
|
|
3104
|
+
WITH node, score
|
|
3105
|
+
WHERE node.isDetail IS NULL OR node.isDetail = false`;
|
|
3106
|
+
export const buildFtsQueryCypher = (tableName, indexName, limit, conjunctive = false) => `
|
|
3107
|
+
CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', $query, conjunctive := ${conjunctive})${tableName === 'Property' ? FTS_DETAIL_FILTER : ''}
|
|
3108
|
+
RETURN node, score
|
|
3109
|
+
ORDER BY score DESC, node.id
|
|
3110
|
+
LIMIT ${limit}
|
|
3111
|
+
`;
|
|
2513
3112
|
/**
|
|
2514
3113
|
* Query a full-text search index
|
|
2515
3114
|
* @param tableName - The node table name
|
|
@@ -2523,12 +3122,7 @@ export const queryFTS = async (tableName, indexName, query, limit = 20, conjunct
|
|
|
2523
3122
|
if (!conn) {
|
|
2524
3123
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
2525
3124
|
}
|
|
2526
|
-
const cypher =
|
|
2527
|
-
CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', $query, conjunctive := ${conjunctive})
|
|
2528
|
-
RETURN node, score
|
|
2529
|
-
ORDER BY score DESC
|
|
2530
|
-
LIMIT ${limit}
|
|
2531
|
-
`;
|
|
3125
|
+
const cypher = buildFtsQueryCypher(tableName, indexName, limit, conjunctive);
|
|
2532
3126
|
try {
|
|
2533
3127
|
const rows = await executePrepared(cypher, { query });
|
|
2534
3128
|
return rows.map((row) => {
|
|
@@ -2544,15 +3138,83 @@ export const queryFTS = async (tableName, indexName, query, limit = 20, conjunct
|
|
|
2544
3138
|
});
|
|
2545
3139
|
}
|
|
2546
3140
|
catch (e) {
|
|
2547
|
-
// Return empty
|
|
2548
|
-
|
|
3141
|
+
// Return empty only for a genuinely-missing index — the ordinary,
|
|
3142
|
+
// expected case. A missing TABLE (schema drift) or any other real error
|
|
3143
|
+
// rethrows instead of being silently swallowed (tri-review Residual-1 /
|
|
3144
|
+
// NEW-6 — this used to be a bare `.includes('does not exist')` check
|
|
3145
|
+
// that could not tell the two apart).
|
|
3146
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3147
|
+
if (classifyFtsQueryError(message) === 'missing-index') {
|
|
2549
3148
|
return [];
|
|
2550
3149
|
}
|
|
2551
3150
|
throw e;
|
|
2552
3151
|
}
|
|
2553
3152
|
};
|
|
2554
3153
|
/**
|
|
2555
|
-
*
|
|
3154
|
+
* True for the two benign "nothing to drop" `DROP_FTS_INDEX` failures —
|
|
3155
|
+
* both catalog/binder exceptions, LadybugDB's classes for "this name isn't
|
|
3156
|
+
* bound to anything right now" (probe-verified end-to-end through
|
|
3157
|
+
* `dropFTSIndex`'s real `conn.query()` path against @ladybugdb/core
|
|
3158
|
+
* 0.18.x): the named index was never created (`Binder exception: Table <T>
|
|
3159
|
+
* doesn't have an index with name <name>.`), or the FTS extension/function
|
|
3160
|
+
* isn't registered at all (`Catalog exception: function DROP_FTS_INDEX is
|
|
3161
|
+
* not defined...`). A real engine failure — e.g. the `Runtime exception:
|
|
3162
|
+
* FTS index '<name>' is inconsistent: ...` class from #2589 — is a
|
|
3163
|
+
* DIFFERENT exception class (an execution-time failure, not a catalog/bind
|
|
3164
|
+
* lookup miss), so this returns false for it. Anchored to the START of the
|
|
3165
|
+
* message (not a bare substring search): every probed LadybugDB error leads
|
|
3166
|
+
* with its exception class, and anchoring means a future message that merely
|
|
3167
|
+
* mentions "Binder exception" or "Catalog exception" further in in the body
|
|
3168
|
+
* of an otherwise-genuine failure can't be misclassified as benign. Pure
|
|
3169
|
+
* string logic so it is unit-testable without a native LadybugDB connection.
|
|
3170
|
+
*/
|
|
3171
|
+
export const isBenignDropFtsIndexError = (message) => message.startsWith('Binder exception:') || message.startsWith('Catalog exception:');
|
|
3172
|
+
/**
|
|
3173
|
+
* The half of {@link isBenignDropFtsIndexError}'s catalog case that means "the
|
|
3174
|
+
* FTS extension is not loaded", as opposed to "this index does not exist".
|
|
3175
|
+
* Benign only when there is genuinely nothing to drop — see `dropFTSIndex`.
|
|
3176
|
+
*/
|
|
3177
|
+
const DROP_FTS_INDEX_UNDEFINED_SIGNATURE = 'function DROP_FTS_INDEX is not defined';
|
|
3178
|
+
/**
|
|
3179
|
+
* Whether `indexName` is currently present on `tableName` in the catalog.
|
|
3180
|
+
*
|
|
3181
|
+
* `unverifiable` ⇒ "an index may be present" (#2841 review H3), and the caller
|
|
3182
|
+
* must treat it exactly as it treats `present`. This used to answer a bare
|
|
3183
|
+
* `false` there — reporting an unprovable catalog as "index absent", the one
|
|
3184
|
+
* meaning {@link readIndexCatalogRows} explicitly forbids — which made
|
|
3185
|
+
* `dropFTSIndex` swallow the very error this guard exists to raise and handed
|
|
3186
|
+
* the caller a drop that never happened, reproducing the silent #2841 crash one
|
|
3187
|
+
* DML statement later. The asymmetric cost settles it: a false positive raises a
|
|
3188
|
+
* loud, FTS-naming, remedy-carrying error on a run that had already lost its
|
|
3189
|
+
* catalog; a false negative resumes a writeback that cannot succeed.
|
|
3190
|
+
*/
|
|
3191
|
+
const ftsIndexPresenceInCatalog = async (tableName, indexName) => {
|
|
3192
|
+
const rows = await readIndexCatalogRows();
|
|
3193
|
+
if (rows === undefined)
|
|
3194
|
+
return 'unverifiable';
|
|
3195
|
+
return rows.some((row) => indexRowTable(row) === tableName && indexRowName(row) === indexName)
|
|
3196
|
+
? 'present'
|
|
3197
|
+
: 'absent';
|
|
3198
|
+
};
|
|
3199
|
+
/**
|
|
3200
|
+
* Drop an FTS index. Tolerates only {@link isBenignDropFtsIndexError} —
|
|
3201
|
+
* anything else rethrows instead of being silently masked, which previously
|
|
3202
|
+
* let a corrupted index persist across analyze runs undetected.
|
|
3203
|
+
*
|
|
3204
|
+
* One benign class is conditional (#2841): `Catalog exception: function
|
|
3205
|
+
* DROP_FTS_INDEX is not defined` says the FTS extension is not loaded, which
|
|
3206
|
+
* is "nothing to drop" only when the named index does not exist. When it DOES
|
|
3207
|
+
* exist, swallowing that error reports a drop that never happened, and the
|
|
3208
|
+
* next insert/delete against that table dies at bind time with an engine
|
|
3209
|
+
* message that never mentions FTS — the #2841 crash. So the liveness question
|
|
3210
|
+
* is settled with a catalog read on the ERROR path only (the healthy path
|
|
3211
|
+
* still costs nothing) and a live-but-undroppable index is raised loudly,
|
|
3212
|
+
* naming FTS and both remedies — the load-side one CLASSIFIED, never
|
|
3213
|
+
* hand-rolled (#2841 review §5.G).
|
|
3214
|
+
*
|
|
3215
|
+
* A catalog that cannot be read blocks identically (see
|
|
3216
|
+
* {@link ftsIndexPresenceInCatalog}) but is reported as an inability to verify,
|
|
3217
|
+
* not as an assertion that the index exists.
|
|
2556
3218
|
*/
|
|
2557
3219
|
export const dropFTSIndex = async (tableName, indexName) => {
|
|
2558
3220
|
if (!conn) {
|
|
@@ -2561,8 +3223,51 @@ export const dropFTSIndex = async (tableName, indexName) => {
|
|
|
2561
3223
|
try {
|
|
2562
3224
|
await queryAndDrain(conn, `CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
|
|
2563
3225
|
}
|
|
2564
|
-
catch {
|
|
2565
|
-
|
|
3226
|
+
catch (e) {
|
|
3227
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3228
|
+
if (!isBenignDropFtsIndexError(msg)) {
|
|
3229
|
+
throw e;
|
|
3230
|
+
}
|
|
3231
|
+
const presence = msg.includes(DROP_FTS_INDEX_UNDEFINED_SIGNATURE)
|
|
3232
|
+
? await ftsIndexPresenceInCatalog(tableName, indexName)
|
|
3233
|
+
: 'absent';
|
|
3234
|
+
if (presence !== 'absent') {
|
|
3235
|
+
// Remedy via the shared classifier, UNCONDITIONALLY and never hand-rolled
|
|
3236
|
+
// (#2841 review §5.G). `extension-load-error.ts` exists because "set
|
|
3237
|
+
// GITNEXUS_LBUG_EXTENSION_INSTALL=auto" is the WRONG advice for the
|
|
3238
|
+
// missing-runtime-dependency class (Windows error 126 / OpenSSL, #2374) —
|
|
3239
|
+
// the file is already on disk and reinstalling is a no-op. This used to
|
|
3240
|
+
// honour the classifier for `missing_dependency` only and hand-write the
|
|
3241
|
+
// other three, which handed a corrupt / wrong-platform extension file the
|
|
3242
|
+
// very "allow one bounded install attempt" advice #2383 removed, and
|
|
3243
|
+
// discarded `missingFileRemedy` / `corruptFileRemedy` outright. Every one
|
|
3244
|
+
// of the four kinds already carries a class-correct remedy by
|
|
3245
|
+
// construction, so take it as-is.
|
|
3246
|
+
//
|
|
3247
|
+
// Prefer the diagnosis cached at mark-unavailable time (#2383 F3) so the
|
|
3248
|
+
// extension binary is not re-inspected, falling back to a fresh structural
|
|
3249
|
+
// diagnosis when nothing recorded one.
|
|
3250
|
+
const ftsCapability = getFtsCapability();
|
|
3251
|
+
const { remedy } = ftsCapability?.diagnosis ?? diagnoseExtensionLoad(ftsCapability?.reason);
|
|
3252
|
+
// Deliberately message-only: `remedy` is generated text (fixed system paths
|
|
3253
|
+
// at most), and LadybugDB's own path-bearing `reason` is NEVER interpolated
|
|
3254
|
+
// here — the #2374/#2375 redaction contract.
|
|
3255
|
+
//
|
|
3256
|
+
// `unverifiable` blocks exactly as hard as `present`, but must not be
|
|
3257
|
+
// WORDED as `present`: the one reachable path into it is a run whose
|
|
3258
|
+
// `ensureFtsRowDmlSafe` already answered "safe" because the catalog showed
|
|
3259
|
+
// no FTS index, after which this later read failed — so asserting the index
|
|
3260
|
+
// exists would contradict what the same run just proved (#2841 cleanup
|
|
3261
|
+
// review).
|
|
3262
|
+
const lead = presence === 'present'
|
|
3263
|
+
? `FTS index '${indexName}' on table ${tableName} exists but the LadybugDB FTS ` +
|
|
3264
|
+
'extension is not loaded, so it cannot be dropped in this environment.'
|
|
3265
|
+
: `FTS index '${indexName}' on table ${tableName} could not be verified as absent — ` +
|
|
3266
|
+
'the LadybugDB index catalog could not be read — and the FTS extension is not ' +
|
|
3267
|
+
'loaded, so the index could not be dropped either.';
|
|
3268
|
+
throw new Error(`${lead} Every insert and delete against that table fails while the index is present. ` +
|
|
3269
|
+
`${remedy} Otherwise rebuild the index without FTS via \`gitnexus analyze --force\`.`);
|
|
3270
|
+
}
|
|
2566
3271
|
}
|
|
2567
3272
|
finally {
|
|
2568
3273
|
ensuredFTSIndexes.delete(ftsIndexKey(tableName, indexName));
|