gitnexus 1.6.10-rc.2 → 1.6.10-rc.21
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 +56 -12
- package/dist/cli/analyze.js +166 -5
- package/dist/cli/clean.js +17 -3
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +18 -0
- package/dist/cli/doctor.js +76 -3
- package/dist/cli/embeddings.d.ts +12 -0
- package/dist/cli/embeddings.js +49 -0
- package/dist/cli/help-i18n.js +4 -0
- package/dist/cli/i18n/en.d.ts +10 -5
- package/dist/cli/i18n/en.js +10 -5
- package/dist/cli/i18n/resources.d.ts +15 -5
- package/dist/cli/i18n/zh-CN.d.ts +5 -0
- package/dist/cli/i18n/zh-CN.js +10 -5
- package/dist/cli/index.js +12 -1
- package/dist/core/augmentation/engine.js +4 -3
- package/dist/core/embeddings/embedder.js +16 -2
- package/dist/core/embeddings/embedding-pipeline.d.ts +20 -0
- package/dist/core/embeddings/embedding-pipeline.js +10 -2
- package/dist/core/embeddings/http-client.d.ts +28 -1
- package/dist/core/embeddings/http-client.js +107 -15
- package/dist/core/embeddings/node-module-compat.d.ts +23 -0
- package/dist/core/embeddings/node-module-compat.js +23 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +6 -3
- package/dist/core/embeddings/onnxruntime-node-resolver.js +31 -5
- package/dist/core/embeddings/runtime-install.d.ts +119 -0
- package/dist/core/embeddings/runtime-install.js +372 -0
- package/dist/core/embeddings/runtime-support.d.ts +48 -17
- package/dist/core/embeddings/runtime-support.js +96 -0
- package/dist/core/group/extractors/http-patterns/python.js +246 -108
- package/dist/core/group/extractors/manifest-extractor.js +4 -0
- package/dist/core/incremental/escalation-gate.d.ts +38 -0
- package/dist/core/incremental/escalation-gate.js +48 -0
- package/dist/core/incremental/shadow-candidates.d.ts +3 -3
- package/dist/core/incremental/shadow-candidates.js +3 -3
- package/dist/core/incremental/subgraph-extract.d.ts +2 -2
- package/dist/core/incremental/subgraph-extract.js +2 -2
- package/dist/core/ingestion/cobol/jcl-processor.js +9 -8
- package/dist/core/ingestion/cobol-processor.js +27 -26
- package/dist/core/ingestion/community-processor.d.ts +51 -1
- package/dist/core/ingestion/community-processor.js +316 -63
- package/dist/core/ingestion/emit-references.js +3 -2
- package/dist/core/ingestion/languages/cpp/captures.js +157 -74
- package/dist/core/ingestion/markdown-processor.js +3 -2
- package/dist/core/ingestion/parsing-processor.d.ts +3 -1
- package/dist/core/ingestion/parsing-processor.js +4 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +42 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +83 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.js +131 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.d.ts +75 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.js +299 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +12 -1
- package/dist/core/ingestion/utils/line-base.d.ts +20 -0
- package/dist/core/ingestion/utils/line-base.js +20 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +21 -0
- package/dist/core/ingestion/utils/symbol-labels.js +45 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +34 -0
- package/dist/core/ingestion/workers/parse-worker.js +40 -5
- package/dist/core/ingestion/workers/result-merge.js +4 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +14 -0
- package/dist/core/ingestion/workers/worker-pool.js +85 -5
- package/dist/core/lbug/csv-generator.js +8 -2
- package/dist/core/lbug/cypher-escape.d.ts +20 -0
- package/dist/core/lbug/cypher-escape.js +20 -0
- package/dist/core/lbug/extension-load-error.d.ts +67 -0
- package/dist/core/lbug/extension-load-error.js +320 -0
- package/dist/core/lbug/extension-loader.d.ts +17 -2
- package/dist/core/lbug/extension-loader.js +38 -13
- package/dist/core/lbug/lbug-adapter.d.ts +147 -1
- package/dist/core/lbug/lbug-adapter.js +390 -70
- package/dist/core/lbug/lbug-config.d.ts +28 -0
- package/dist/core/lbug/lbug-config.js +114 -4
- package/dist/core/lbug/native-check.d.ts +22 -0
- package/dist/core/lbug/native-check.js +66 -0
- package/dist/core/lbug/pool-adapter.js +10 -1
- package/dist/core/lbug/sidecar-recovery.d.ts +172 -0
- package/dist/core/lbug/sidecar-recovery.js +418 -16
- package/dist/core/platform/capabilities.js +27 -1
- package/dist/core/run-analyze.js +470 -108
- package/dist/core/search/fts-indexes.d.ts +7 -0
- package/dist/core/search/fts-indexes.js +37 -0
- package/dist/core/tree-sitter/safe-parse.d.ts +8 -3
- package/dist/core/tree-sitter/safe-parse.js +21 -7
- package/dist/core/wiki/graph-queries.js +6 -5
- package/dist/mcp/core/embedder.js +16 -2
- package/dist/mcp/local/line-display.d.ts +22 -0
- package/dist/mcp/local/line-display.js +3 -0
- package/dist/mcp/local/local-backend.d.ts +14 -0
- package/dist/mcp/local/local-backend.js +63 -19
- package/dist/mcp/local/pdg-impact.d.ts +5 -3
- package/dist/mcp/local/pdg-impact.js +5 -2
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/server/analyze-launch.d.ts +6 -0
- package/dist/server/analyze-launch.js +82 -6
- package/dist/server/api.d.ts +30 -0
- package/dist/server/api.js +63 -21
- package/dist/storage/parse-cache.js +1 -1
- package/dist/storage/repo-manager.d.ts +65 -1
- package/dist/storage/repo-manager.js +18 -1
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +50 -3
- package/hooks/claude/gitnexus-hook.cjs +59 -9
- package/package.json +5 -3
- package/scripts/cross-platform-tests.ts +43 -0
- package/scripts/ensure-fts.ts +32 -0
- package/scripts/install-duckdb-extension.mjs +85 -24
- package/scripts/run-cross-platform.ts +46 -6
- package/scripts/shard-arg.ts +30 -0
- package/web/assets/ProcessFlowModal-Dna7GkwO.js +22 -0
- package/web/assets/{agent-Dli_x7bU.js → agent-DNYDy-KA.js} +49 -49
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BVWFvYz2.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-BU5ZRITg.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-BkWhpzWB.js → blockDiagram-7IZFK4PR-Dut1BpTV.js} +2 -2
- package/web/assets/{c4Diagram-Y2BXMSZH-DtBvpzWD.js → c4Diagram-Y2BXMSZH-DOXVs7-C.js} +2 -2
- package/web/assets/chunk-2T2R6R2M-pkw2oXHH.js +4 -0
- package/web/assets/chunk-2UTLFMKG-CXlhF3Pt.js +1 -0
- package/web/assets/chunk-3SSMPTDK-rPsRM1x0.js +321 -0
- package/web/assets/chunk-4R4BOZG6-Be_F0JoX.js +159 -0
- package/web/assets/{chunk-6764PJDD-DpIS7hxD.js → chunk-6764PJDD-C7xxk063.js} +1 -1
- package/web/assets/{chunk-67TQ5CYL-4sNPJU1q.js → chunk-67TQ5CYL-zxhDWgyF.js} +3 -3
- package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +32 -0
- package/web/assets/chunk-7J6CGLKN-AmkN8jJN.js +10 -0
- package/web/assets/chunk-7W6UQGC5-pyKGssV3.js +1 -0
- package/web/assets/chunk-C62D2QBJ-3gdL8EKc.js +1 -0
- package/web/assets/chunk-CEXFNPSA-CjwtMDkc.js +1 -0
- package/web/assets/{chunk-INKRHTLW-Nu4ri9P-.js → chunk-INKRHTLW-D9BM9HWZ.js} +1 -1
- package/web/assets/chunk-J5EP6P6S-L19oz55R.js +1 -0
- package/web/assets/{chunk-KGFNY3KK-JGMJvKCK.js → chunk-KGFNY3KK-tD2F6wxK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-zm6n242I.js +1 -0
- package/web/assets/{chunk-KRXBNO2N-BW5o0KUN.js → chunk-KRXBNO2N-C-UNuKKf.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-CfLLCywY.js → chunk-LCXTWHL2-Ba9JK-6F.js} +2 -2
- package/web/assets/{chunk-LII3EMHJ-D-jm-dLa.js → chunk-LII3EMHJ-EGx-eBIu.js} +1 -1
- package/web/assets/chunk-LRIF4GLE-C3_PqUBq.js +1 -0
- package/web/assets/{chunk-QA3QBVWF-BgRW3SXd.js → chunk-QA3QBVWF-D4cSJBf6.js} +1 -1
- package/web/assets/chunk-RERM46MO-B5dAdWpP.js +1 -0
- package/web/assets/{chunk-RG4AUYOV-CqWzWsV3.js → chunk-RG4AUYOV-OqBBMwuQ.js} +4 -4
- package/web/assets/chunk-RKZBBQEN-MyIQbGgD.js +1 -0
- package/web/assets/chunk-RLI5ZMPA-CkXZ0y0U.js +1 -0
- package/web/assets/{chunk-T2UQINTJ-DSBqkzH7.js → chunk-T2UQINTJ-BJusEiGe.js} +1 -1
- package/web/assets/chunk-UP6H54XL-BG-n7qDp.js +1 -0
- package/web/assets/chunk-UXSXWOXI-CW1_r5cS.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-DyF0vyGd.js → chunk-UY5QBCOK-BCpUlARQ.js} +1 -1
- package/web/assets/chunk-VU6ZFW4Y-BeXVk45_.js +2 -0
- package/web/assets/{chunk-W44A43WB-JqMLIpR0.js → chunk-W44A43WB-BN8EQc_u.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CxN8oiwI.js → chunk-ZXARS5L4-BI_qR2Nm.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-D9zSIg0x.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-D9zSIg0x.js +1 -0
- package/web/assets/cose-bilkent-UX7MHV2Q-BmnZ7Mvj.js +1 -0
- package/web/assets/dagre-ND4H6XIP-CpcV5kbl.js +4 -0
- package/web/assets/diagram-3NCE3AQN-6SMxBRpj.js +43 -0
- package/web/assets/diagram-GF46GFSD-BZEdNcRZ.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-DFCfd5LI.js → diagram-HNR7UZ2L-DxKW2a2Y.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-HfTHcXX_.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-CyijUS8r.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-8X1y6R4Y.js → erDiagram-L5TCEMPS-D0fvpWEX.js} +3 -3
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-CgFBM7e-.js +1 -0
- package/web/assets/flowDiagram-H6V6AXG4-Bvr9bbhk.js +162 -0
- package/web/assets/{ganttDiagram-JCBTUEKG-D3iL2Aet.js → ganttDiagram-JCBTUEKG-TzODrLzv.js} +2 -2
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DCJv7SII.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-ONQSwJIY.js +106 -0
- package/web/assets/index-B4eB4dNZ.js +575 -0
- package/web/assets/index-CX_fADmQ.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-BfNy-9Nd.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-DIPVUIQ6.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-DfYpxnE5.js → ishikawaDiagram-BNXS4ZKH-BLjivjXL.js} +3 -3
- package/web/assets/{journeyDiagram-M6C3CM3L-CvArszLo.js → journeyDiagram-M6C3CM3L-Cetgs3NH.js} +3 -3
- package/web/assets/{kanban-definition-75IXJCU3-CpfVLLf9.js → kanban-definition-75IXJCU3-DMlCaIpf.js} +5 -5
- package/web/assets/{katex-K3KEBU37-CTc5BslQ.js → katex-K3KEBU37-qRQBy-EZ.js} +2 -2
- package/web/assets/{mindmap-definition-2TDM6QVE-DypvbOMQ.js → mindmap-definition-2TDM6QVE-B4GbXQBc.js} +3 -3
- package/web/assets/packet-YPE3B663-LP52Z2RK-D4qkeG9k.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-B_yMKyQa.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-s27EFWgP.js → pieDiagram-CU6KROY3-D3YC_xyg.js} +2 -2
- package/web/assets/{quadrantDiagram-VICAPDV7-yXSZ2lLz.js → quadrantDiagram-VICAPDV7-B1TQnlm0.js} +2 -2
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DeD6yN_0.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-CWhqInuf.js → requirementDiagram-JXO7QTGE-f8wczLQf.js} +3 -3
- package/web/assets/rolldown-runtime-QTnfLwEv.js +1 -0
- package/web/assets/{sankeyDiagram-URQDO5SZ-1qEjlFIB.js → sankeyDiagram-URQDO5SZ-BTxzG5j0.js} +2 -2
- package/web/assets/{sequenceDiagram-VS2MUI6T-RyTlZ5cR.js → sequenceDiagram-VS2MUI6T-DuamM-IR.js} +5 -5
- package/web/assets/src-BNCXICdA.js +1 -0
- package/web/assets/stateDiagram-7D4R322I-Ch2pEUcw.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-7ANuoZLL.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-DrrqLYAf.js → timeline-definition-O6YCAMPW-07BR8R8G.js} +3 -3
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CDuSYnd4.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-imazucyJ.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-B0RkWAim.js → vennDiagram-MWXL3ELB-BQX2ossk.js} +4 -4
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-DqrT9tB8.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-boUxiQ5F.js → wardleyDiagram-CUQ6CDDI-CGIJBBiK.js} +3 -3
- package/web/assets/{xychartDiagram-N2JHSOCM-BOLm0eNm.js → xychartDiagram-N2JHSOCM-B3HNMuWp.js} +4 -4
- package/web/index.html +17 -10
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-dEkqUZN9.js +0 -36
- package/web/assets/chunk-3SSMPTDK-B4_etUhr.js +0 -321
- package/web/assets/chunk-7W6UQGC5-KyEG0HQg.js +0 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +0 -161
- package/web/assets/classDiagram-KGZ6W3CR-CMne4tG9.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CMne4tG9.js +0 -1
- package/web/assets/cose-bilkent-UX7MHV2Q-D71wNYRJ.js +0 -1
- package/web/assets/dagre-ND4H6XIP-Co5rRx9X.js +0 -4
- package/web/assets/diagram-3NCE3AQN-DBp4O00j.js +0 -43
- package/web/assets/diagram-GF46GFSD-6N_8yS0i.js +0 -24
- package/web/assets/diagram-QXG6HAR7-Di093rFg.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-DZWi4SUN.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +0 -1
- package/web/assets/flowDiagram-H6V6AXG4-CMpY8Ufn.js +0 -162
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +0 -106
- package/web/assets/index-KctnI3Vq.js +0 -635
- package/web/assets/index-_lgn7hs5.css +0 -2
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-BaLMZkUg.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-DTjVfX4A.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-xY9_k9_Q.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +0 -1
|
@@ -62,6 +62,31 @@ export declare function isWalCorruptionError(err: unknown): boolean;
|
|
|
62
62
|
* back to the permissive matcher.
|
|
63
63
|
*/
|
|
64
64
|
export declare const isLbugCheckpointIoError: (err: unknown) => boolean;
|
|
65
|
+
/**
|
|
66
|
+
* True when `err` looks like the LadybugDB buffer manager failing to release
|
|
67
|
+
* frame memory — the failure mode of a 4 KiB page-size assumption on a
|
|
68
|
+
* 16 KiB/64 KiB-page kernel (#1231). Deliberately does NOT match the
|
|
69
|
+
* generic "buffer pool is full" exhaustion error, which is a sizing
|
|
70
|
+
* problem, not a page-size one.
|
|
71
|
+
*/
|
|
72
|
+
export declare const isLbugPageSizeFrameError: (err: unknown) => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* True when the given `@ladybugdb/core` version contains the runtime
|
|
75
|
+
* OS-page-size detection introduced in 0.18.0 (see the matcher comment
|
|
76
|
+
* above). Unknown/unparseable versions return false so callers err on the
|
|
77
|
+
* side of showing the upgrade hint.
|
|
78
|
+
*/
|
|
79
|
+
export declare const isPageSizeAwareLadybug: (version: string | undefined) => boolean;
|
|
80
|
+
/**
|
|
81
|
+
* OS memory page size in bytes, or `undefined` when it cannot be determined
|
|
82
|
+
* (Windows, missing getconf, sandboxed exec). Node exposes no page-size API,
|
|
83
|
+
* so this shells out to POSIX `getconf PAGE_SIZE` — same execFileSync shape
|
|
84
|
+
* as the Windows 8.3 short-path probe above, but with a tighter timeout and
|
|
85
|
+
* an explicit killSignal (see the options comment below).
|
|
86
|
+
*/
|
|
87
|
+
export declare const getOsPageSize: () => number | undefined;
|
|
88
|
+
/** Exported only for unit tests — clears the getconf probe cache. */
|
|
89
|
+
export declare const _resetOsPageSizeCacheForTest: () => void;
|
|
65
90
|
type LbugModule = typeof lbug;
|
|
66
91
|
export interface LbugDatabaseOptions {
|
|
67
92
|
readOnly?: boolean;
|
|
@@ -108,6 +133,8 @@ export type DeleteAllErrorClass = 'benign-missing-table' | 'rethrow';
|
|
|
108
133
|
*/
|
|
109
134
|
export declare const classifyDeleteAllError: (err: unknown) => DeleteAllErrorClass;
|
|
110
135
|
export declare function createLbugDatabase(lbugModule: LbugModule, databasePath: string, options?: LbugDatabaseOptions): lbug.Database;
|
|
136
|
+
export declare const HANDLE_RELEASE_PROBE_ATTEMPTS = 5;
|
|
137
|
+
export declare const HANDLE_RELEASE_PROBE_DELAY_MS = 50;
|
|
111
138
|
/**
|
|
112
139
|
* Marker symbol attached to lock errors after `openWithLockRetry` exhausts
|
|
113
140
|
* its budget. `withLbugDb`'s outer query-time retry consults this so it
|
|
@@ -121,6 +148,7 @@ export declare const LBUG_OPEN_RETRY_EXHAUSTED: unique symbol;
|
|
|
121
148
|
export declare const isOpenRetryExhausted: (err: unknown) => boolean;
|
|
122
149
|
/** Exported only for direct unit testing — production callers use `openWithLockRetry`. */
|
|
123
150
|
export declare const _isTestFixturePathForTest: (dbPath: string) => boolean;
|
|
151
|
+
export declare const sleep: (ms: number) => Promise<void>;
|
|
124
152
|
export declare function openLbugConnection(lbugModule: LbugModule, databasePath: string, options?: LbugDatabaseOptions): Promise<LbugConnectionHandle>;
|
|
125
153
|
export declare function closeLbugConnection(handle: LbugConnectionHandle): Promise<void>;
|
|
126
154
|
/**
|
|
@@ -337,6 +337,106 @@ export const isLbugCheckpointIoError = (err) => {
|
|
|
337
337
|
return true;
|
|
338
338
|
return LBUG_CHECKPOINT_PERMISSIVE_RE.test(msg);
|
|
339
339
|
};
|
|
340
|
+
// ─── Ladybug non-4K page-size frame-release matcher (#1231) ─────────────────
|
|
341
|
+
//
|
|
342
|
+
// LadybugDB <= 0.17.x hardcoded a 4 KiB OS-page assumption in its buffer
|
|
343
|
+
// manager: evicting a frame released physical memory with
|
|
344
|
+
// `madvise(frame, frameSize, MADV_DONTNEED)` on 4 KiB-aligned frame
|
|
345
|
+
// addresses (verified by disassembling `VMRegion::releaseFrame` in
|
|
346
|
+
// @ladybugdb/core-linux-arm64 0.17.1 — `mov w2, #0x4` = MADV_DONTNEED,
|
|
347
|
+
// throw on non-zero return). On kernels with 16 KiB pages (Raspberry Pi 5
|
|
348
|
+
// default 2712 kernel, Asahi Linux) or 64 KiB pages (some enterprise arm64
|
|
349
|
+
// distros), madvise rejects addresses that are not multiples of the real
|
|
350
|
+
// page size with EINVAL, surfacing as:
|
|
351
|
+
// "Buffer manager exception: Releasing physical memory associated with a
|
|
352
|
+
// frame failed with error code -1: Invalid argument."
|
|
353
|
+
// which aborts `gitnexus analyze` mid-COPY.
|
|
354
|
+
//
|
|
355
|
+
// @ladybugdb/core 0.18.0 rewrote the release path with runtime OS-page-size
|
|
356
|
+
// detection and discard-granule-aligned madvise (new binary strings:
|
|
357
|
+
// "Failed to detect the operating system page size.", "Unsupported page
|
|
358
|
+
// size combination: frame size {}, discard granule size {}, frame group
|
|
359
|
+
// size {}."), so upgrading is the fix. The residual 0.18.0 guard
|
|
360
|
+
// ("Unsupported page size combination") is matched here too so exotic
|
|
361
|
+
// configurations receive the same actionable guidance instead of a raw
|
|
362
|
+
// native message.
|
|
363
|
+
const LBUG_FRAME_RELEASE_RE = /releasing physical memory associated with a frame failed/i;
|
|
364
|
+
const LBUG_PAGE_COMBO_RE = /unsupported page size combination/i;
|
|
365
|
+
/**
|
|
366
|
+
* True when `err` looks like the LadybugDB buffer manager failing to release
|
|
367
|
+
* frame memory — the failure mode of a 4 KiB page-size assumption on a
|
|
368
|
+
* 16 KiB/64 KiB-page kernel (#1231). Deliberately does NOT match the
|
|
369
|
+
* generic "buffer pool is full" exhaustion error, which is a sizing
|
|
370
|
+
* problem, not a page-size one.
|
|
371
|
+
*/
|
|
372
|
+
export const isLbugPageSizeFrameError = (err) => {
|
|
373
|
+
if (!err)
|
|
374
|
+
return false;
|
|
375
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
376
|
+
return LBUG_FRAME_RELEASE_RE.test(msg) || LBUG_PAGE_COMBO_RE.test(msg);
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* True when the given `@ladybugdb/core` version contains the runtime
|
|
380
|
+
* OS-page-size detection introduced in 0.18.0 (see the matcher comment
|
|
381
|
+
* above). Unknown/unparseable versions return false so callers err on the
|
|
382
|
+
* side of showing the upgrade hint.
|
|
383
|
+
*/
|
|
384
|
+
export const isPageSizeAwareLadybug = (version) => {
|
|
385
|
+
if (!version)
|
|
386
|
+
return false;
|
|
387
|
+
const m = /^(\d+)\.(\d+)/.exec(version.trim());
|
|
388
|
+
if (!m)
|
|
389
|
+
return false;
|
|
390
|
+
const major = Number(m[1]);
|
|
391
|
+
const minor = Number(m[2]);
|
|
392
|
+
return major > 0 || minor >= 18;
|
|
393
|
+
};
|
|
394
|
+
// `undefined` = not probed yet; `null` = probed and unavailable. Cached
|
|
395
|
+
// because analyze error paths and doctor may both ask, and getconf forks.
|
|
396
|
+
let cachedOsPageSize;
|
|
397
|
+
/**
|
|
398
|
+
* OS memory page size in bytes, or `undefined` when it cannot be determined
|
|
399
|
+
* (Windows, missing getconf, sandboxed exec). Node exposes no page-size API,
|
|
400
|
+
* so this shells out to POSIX `getconf PAGE_SIZE` — same execFileSync shape
|
|
401
|
+
* as the Windows 8.3 short-path probe above, but with a tighter timeout and
|
|
402
|
+
* an explicit killSignal (see the options comment below).
|
|
403
|
+
*/
|
|
404
|
+
export const getOsPageSize = () => {
|
|
405
|
+
if (cachedOsPageSize !== undefined)
|
|
406
|
+
return cachedOsPageSize ?? undefined;
|
|
407
|
+
if (process.platform === 'win32') {
|
|
408
|
+
// Windows allocation granularity is not what madvise alignment is about;
|
|
409
|
+
// the #1231 failure mode is POSIX-only.
|
|
410
|
+
cachedOsPageSize = null;
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
// killSignal SIGKILL (first use in this repo): the default SIGTERM is
|
|
415
|
+
// catchable, so a signal-trapping child held the "5s" timeout for 9s in
|
|
416
|
+
// review reproduction — SIGKILL makes the timeout real for everything
|
|
417
|
+
// except a child stuck in uninterruptible I/O (D state). 2000ms, not
|
|
418
|
+
// 5000: doctor runs this probe on its happy path and real getconf
|
|
419
|
+
// answers in ~2ms, but keep margin for loaded Pi-class hardware — a
|
|
420
|
+
// too-tight ceiling would silently drop the very #1231 diagnostics this
|
|
421
|
+
// probe exists to provide (the catch caches the failure). (#2424 review)
|
|
422
|
+
const out = execFileSync('getconf', ['PAGE_SIZE'], {
|
|
423
|
+
encoding: 'utf-8',
|
|
424
|
+
timeout: 2000,
|
|
425
|
+
killSignal: 'SIGKILL',
|
|
426
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
427
|
+
});
|
|
428
|
+
const parsed = Number(out.trim());
|
|
429
|
+
cachedOsPageSize = Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
cachedOsPageSize = null;
|
|
433
|
+
}
|
|
434
|
+
return cachedOsPageSize ?? undefined;
|
|
435
|
+
};
|
|
436
|
+
/** Exported only for unit tests — clears the getconf probe cache. */
|
|
437
|
+
export const _resetOsPageSizeCacheForTest = () => {
|
|
438
|
+
cachedOsPageSize = undefined;
|
|
439
|
+
};
|
|
340
440
|
/**
|
|
341
441
|
* Return true when the error message indicates that a LadybugDB write
|
|
342
442
|
* transaction could not proceed due to lock contention — either a file
|
|
@@ -411,7 +511,10 @@ export function createLbugDatabase(lbugModule, databasePath, options = {}) {
|
|
|
411
511
|
// 1. OPEN_LOCK_RETRY_ATTEMPTS / OPEN_LOCK_RETRY_DELAY_MS (this file)
|
|
412
512
|
// → `new lbug.Database()` constructor lock failures
|
|
413
513
|
// 2. HANDLE_RELEASE_PROBE_ATTEMPTS / HANDLE_RELEASE_PROBE_DELAY_MS (this file)
|
|
414
|
-
// → post-close fs.open probe to absorb Windows handle-release lag
|
|
514
|
+
// → post-close fs.open probe to absorb Windows handle-release lag; also
|
|
515
|
+
// the shared budget for wipeLbugDbFiles' ENOENT-verified removal
|
|
516
|
+
// (lbug-adapter.ts) and the dirty-recovery sidecar park's
|
|
517
|
+
// rename/rm retries (sidecar-recovery.ts) — same lock class
|
|
415
518
|
// 3. DB_LOCK_RETRY_ATTEMPTS / DB_LOCK_RETRY_DELAY_MS (lbug-adapter.ts withLbugDb)
|
|
416
519
|
// → query-time busy/lock retry around already-open connections
|
|
417
520
|
//
|
|
@@ -429,8 +532,13 @@ export function createLbugDatabase(lbugModule, databasePath, options = {}) {
|
|
|
429
532
|
// matches on is unchanged.)
|
|
430
533
|
const OPEN_LOCK_RETRY_ATTEMPTS = 5;
|
|
431
534
|
const OPEN_LOCK_RETRY_DELAY_MS = 100;
|
|
432
|
-
|
|
433
|
-
|
|
535
|
+
// Exported (this shipping review, FIX 1/2): the dirty-recovery sidecar park
|
|
536
|
+
// (sidecar-recovery.ts) and the ENOENT-verified wipe (lbug-adapter.ts
|
|
537
|
+
// wipeLbugDbFiles) retry the SAME Windows handle-release/AV lock class, and
|
|
538
|
+
// their previous private mirror constants were documentation-coupled copies
|
|
539
|
+
// that could drift from this tuning-knob registry silently.
|
|
540
|
+
export const HANDLE_RELEASE_PROBE_ATTEMPTS = 5;
|
|
541
|
+
export const HANDLE_RELEASE_PROBE_DELAY_MS = 50;
|
|
434
542
|
const HANDLE_RELEASE_LOCK_CODES = new Set(['EBUSY', 'EPERM', 'EACCES']);
|
|
435
543
|
/**
|
|
436
544
|
* Test-fixture directory prefixes recognized by `isTestFixturePath`.
|
|
@@ -492,7 +600,9 @@ const isTestFixturePath = (dbPath) => {
|
|
|
492
600
|
};
|
|
493
601
|
/** Exported only for direct unit testing — production callers use `openWithLockRetry`. */
|
|
494
602
|
export const _isTestFixturePathForTest = isTestFixturePath;
|
|
495
|
-
|
|
603
|
+
// Exported alongside HANDLE_RELEASE_PROBE_* (this shipping review, FIX 1/2)
|
|
604
|
+
// so the consumers of the shared retry budget do not each grow a private copy.
|
|
605
|
+
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
496
606
|
/**
|
|
497
607
|
* Attempt to remove stale `.wal` / `.lock` sidecars that a previous aborted
|
|
498
608
|
* test run may have left behind. Best-effort: ENOENT is normal, anything
|
|
@@ -4,3 +4,25 @@ export interface NativeCheckResult {
|
|
|
4
4
|
message?: string;
|
|
5
5
|
}
|
|
6
6
|
export declare function checkLbugNative(overridePkgDir?: string): NativeCheckResult;
|
|
7
|
+
export interface FtsProbeResult {
|
|
8
|
+
loaded: boolean;
|
|
9
|
+
/** Collapsed LadybugDB error when `loaded` is false. */
|
|
10
|
+
reason?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Live-probe `LOAD EXTENSION fts` on a throwaway in-memory database.
|
|
14
|
+
*
|
|
15
|
+
* `doctor` used to print the static platform capability, which contradicted
|
|
16
|
+
* analyze whenever the extension file was missing or unloadable (#2374).
|
|
17
|
+
* LOAD never touches the network, so the probe is safe offline, and it
|
|
18
|
+
* surfaces LadybugDB's real error — which distinguishes a missing extension
|
|
19
|
+
* file from a present-but-broken one (wrong platform, truncated download).
|
|
20
|
+
* Dynamic import so doctor still runs when the native module itself is broken.
|
|
21
|
+
*
|
|
22
|
+
* Bounded by `timeoutMs`: an unresponsive extension file (e.g. on a hung
|
|
23
|
+
* network home dir) must never freeze `doctor` — the tool the degradation
|
|
24
|
+
* warnings send users to. `Promise.race` lets doctor report and move on; it
|
|
25
|
+
* cannot cancel an in-flight native call, so a future thread-blocking case
|
|
26
|
+
* would need an out-of-process probe.
|
|
27
|
+
*/
|
|
28
|
+
export declare function probeFtsExtensionLoad(timeoutMs?: number): Promise<FtsProbeResult>;
|
|
@@ -81,3 +81,69 @@ export function checkLbugNative(overridePkgDir) {
|
|
|
81
81
|
}
|
|
82
82
|
return { ok: true, binaryPath };
|
|
83
83
|
}
|
|
84
|
+
const DEFAULT_FTS_PROBE_TIMEOUT_MS = 10_000;
|
|
85
|
+
/** Close each result, swallowing close-time errors so a successful LOAD is not
|
|
86
|
+
* misreported as a failure (native-check keeps no static lbug dependency, so it
|
|
87
|
+
* cannot reuse the adapter's closeQueryResults — that would eagerly load the
|
|
88
|
+
* module and defeat the dynamic import below). */
|
|
89
|
+
const closeProbeResults = (result) => {
|
|
90
|
+
for (const r of Array.isArray(result) ? result : [result]) {
|
|
91
|
+
try {
|
|
92
|
+
r?.close?.();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// ignore — a close failure must not flip a successful LOAD to failed
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Live-probe `LOAD EXTENSION fts` on a throwaway in-memory database.
|
|
101
|
+
*
|
|
102
|
+
* `doctor` used to print the static platform capability, which contradicted
|
|
103
|
+
* analyze whenever the extension file was missing or unloadable (#2374).
|
|
104
|
+
* LOAD never touches the network, so the probe is safe offline, and it
|
|
105
|
+
* surfaces LadybugDB's real error — which distinguishes a missing extension
|
|
106
|
+
* file from a present-but-broken one (wrong platform, truncated download).
|
|
107
|
+
* Dynamic import so doctor still runs when the native module itself is broken.
|
|
108
|
+
*
|
|
109
|
+
* Bounded by `timeoutMs`: an unresponsive extension file (e.g. on a hung
|
|
110
|
+
* network home dir) must never freeze `doctor` — the tool the degradation
|
|
111
|
+
* warnings send users to. `Promise.race` lets doctor report and move on; it
|
|
112
|
+
* cannot cancel an in-flight native call, so a future thread-blocking case
|
|
113
|
+
* would need an out-of-process probe.
|
|
114
|
+
*/
|
|
115
|
+
export async function probeFtsExtensionLoad(timeoutMs = DEFAULT_FTS_PROBE_TIMEOUT_MS) {
|
|
116
|
+
let timer;
|
|
117
|
+
const timeout = new Promise((resolve) => {
|
|
118
|
+
timer = setTimeout(() => resolve({
|
|
119
|
+
loaded: false,
|
|
120
|
+
reason: 'probe timed out — extension file or filesystem unresponsive',
|
|
121
|
+
}), timeoutMs);
|
|
122
|
+
});
|
|
123
|
+
const probe = (async () => {
|
|
124
|
+
try {
|
|
125
|
+
const { default: lbug } = await import('@ladybugdb/core');
|
|
126
|
+
const db = new lbug.Database(':memory:');
|
|
127
|
+
// Nested finallys so `db` is closed even if the Connection ctor throws.
|
|
128
|
+
try {
|
|
129
|
+
const conn = new lbug.Connection(db);
|
|
130
|
+
try {
|
|
131
|
+
const result = await conn.query('LOAD EXTENSION fts');
|
|
132
|
+
closeProbeResults(result);
|
|
133
|
+
return { loaded: true };
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
await conn.close().catch(() => { });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
await db.close().catch(() => { });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
145
|
+
return { loaded: false, reason: message.replace(/\s+/g, ' ').trim() };
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
return await Promise.race([probe, timeout]).finally(() => clearTimeout(timer));
|
|
149
|
+
}
|
|
@@ -19,7 +19,7 @@ import lbug from '@ladybugdb/core';
|
|
|
19
19
|
import { isReadOnlyDbError, loadFTSExtension } from './lbug-adapter.js';
|
|
20
20
|
import { closeQueryResults } from './query-result-utils.js';
|
|
21
21
|
import { createLbugDatabase, isWalCorruptionError, toNativeSafePath, WAL_RECOVERY_SUGGESTION, } from './lbug-config.js';
|
|
22
|
-
import { isMissingFsError, isMissingShadowSidecarError, isReadOnlyShadowReplayError, preflightLbugSidecars, quarantineWalForMissingShadow, renameFailureMessage, statIfExists, } from './sidecar-recovery.js';
|
|
22
|
+
import { guardWalQuarantine, isMissingFsError, isMissingShadowSidecarError, isReadOnlyShadowReplayError, preflightLbugSidecars, quarantineWalForMissingShadow, renameFailureMessage, statIfExists, } from './sidecar-recovery.js';
|
|
23
23
|
const pool = new Map();
|
|
24
24
|
const poolCloseListeners = new Set();
|
|
25
25
|
/**
|
|
@@ -349,6 +349,12 @@ const poolSidecarLogger = {
|
|
|
349
349
|
* See plan: docs/plans/2026-05-21-001-fix-pr-1747-quarantine-enoent-and-large-wal-plan.md (U2)
|
|
350
350
|
*/
|
|
351
351
|
async function tryQuarantineForMissingShadow(dbPath, opts) {
|
|
352
|
+
// Refuse (throw) before renaming a live WAL when the shadow is present on
|
|
353
|
+
// disk or the orphan WAL is too large — parity with the serve path's
|
|
354
|
+
// refuseLargeWalQuarantine (issue #2382 review, Finding B). Kept OUTSIDE the
|
|
355
|
+
// try so the actionable recovery message propagates to the MCP caller rather
|
|
356
|
+
// than being re-wrapped as a rename failure.
|
|
357
|
+
await guardWalQuarantine(dbPath, opts.reason, opts.err, poolSidecarLogger);
|
|
352
358
|
try {
|
|
353
359
|
const quarantinePath = await quarantineWalForMissingShadow(dbPath, {
|
|
354
360
|
logger: poolSidecarLogger,
|
|
@@ -396,6 +402,7 @@ async function replayShadowPagesWithWritableOpen(dbPath) {
|
|
|
396
402
|
if (isMissingShadowSidecarError(err)) {
|
|
397
403
|
await tryQuarantineForMissingShadow(dbPath, {
|
|
398
404
|
reason: 'pool writable replay recovery',
|
|
405
|
+
err,
|
|
399
406
|
});
|
|
400
407
|
return;
|
|
401
408
|
}
|
|
@@ -429,6 +436,7 @@ async function openReadOnlyDatabase(dbPath) {
|
|
|
429
436
|
db = undefined;
|
|
430
437
|
await tryQuarantineForMissingShadow(dbPath, {
|
|
431
438
|
reason: 'pool read-only recovery',
|
|
439
|
+
err,
|
|
432
440
|
});
|
|
433
441
|
await preflightLbugSidecars(dbPath, {
|
|
434
442
|
mode: 'read-only',
|
|
@@ -559,6 +567,7 @@ async function doInitLbug(repoId, dbPath) {
|
|
|
559
567
|
}
|
|
560
568
|
}
|
|
561
569
|
if (lastError.message.startsWith('LadybugDB checkpoint sidecar is missing') ||
|
|
570
|
+
lastError.message.startsWith('LadybugDB checkpoint sidecar is present but unreachable') ||
|
|
562
571
|
lastError.message.startsWith('GitNexus could not move the LadybugDB WAL sidecar') ||
|
|
563
572
|
isMissingShadowSidecarError(lastError)) {
|
|
564
573
|
throw lastError;
|
|
@@ -32,7 +32,31 @@ export declare const statIfExists: (filePath: string) => Promise<{
|
|
|
32
32
|
export declare const isMissingShadowSidecarError: (err: unknown) => boolean;
|
|
33
33
|
export declare const isReadOnlyShadowReplayError: (err: unknown) => boolean;
|
|
34
34
|
export declare const shadowSidecarRecoveryMessage: (dbPath: string, err: unknown) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Actionable message for the case where LadybugDB reports a "missing shadow"
|
|
37
|
+
* but `inspectLbugSidecars` finds the `.shadow` PRESENT on disk — the open
|
|
38
|
+
* failed on path reachability or a lock, not a genuinely-missing sidecar (issue
|
|
39
|
+
* #2382 review, S2). Unlike `shadowSidecarRecoveryMessage` it does NOT tell the
|
|
40
|
+
* operator to rebuild the index (the remedy is fixing the lock/path). Keeps the
|
|
41
|
+
* `Original error:` tail so downstream `isMissingShadowSidecarError` recognition
|
|
42
|
+
* still matches the wrapped error.
|
|
43
|
+
*/
|
|
44
|
+
export declare const presentShadowUnreachableMessage: (dbPath: string, err: unknown) => string;
|
|
35
45
|
export declare const isPermissionRenameError: (err: unknown) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Canonical remediation guidance for the LadybugDB file-lock class
|
|
48
|
+
* (EBUSY/EPERM/EACCES — an MCP/serve process holding the index, or an
|
|
49
|
+
* antivirus scan). One exported producer (this shipping review, FIX 7):
|
|
50
|
+
* the dirty-recovery park warning below, `LbugWipeError`'s message builder
|
|
51
|
+
* (lbug-adapter.ts) and {@link renameFailureMessage} previously carried
|
|
52
|
+
* three divergent hand-written copies of the same advice.
|
|
53
|
+
*
|
|
54
|
+
* `rerun` names the command to retry once the lock clears — the analyze
|
|
55
|
+
* wipe/park surfaces re-run the analyze; the read-path quarantine surface
|
|
56
|
+
* re-runs whatever command failed. No trailing period: callers own the
|
|
57
|
+
* sentence end.
|
|
58
|
+
*/
|
|
59
|
+
export declare const lbugLockRemediation: (rerun?: string) => string;
|
|
36
60
|
/**
|
|
37
61
|
* Classify a failure surfaced by quarantine rename into an actionable user-facing
|
|
38
62
|
* message.
|
|
@@ -50,6 +74,28 @@ export declare const isPermissionRenameError: (err: unknown) => boolean;
|
|
|
50
74
|
*/
|
|
51
75
|
export declare const renameFailureMessage: (dbPath: string, err: unknown) => string;
|
|
52
76
|
export declare function inspectLbugSidecars(dbPath: string): Promise<LbugSidecarState>;
|
|
77
|
+
/**
|
|
78
|
+
* Reject the WAL-quarantine path when discarding the WAL would be unsafe or
|
|
79
|
+
* wrong. Shared by every reactive missing-shadow recovery consumer — serve (via
|
|
80
|
+
* lbug-adapter's `refuseLargeWalQuarantine`) and the MCP/wiki/augmentation pool
|
|
81
|
+
* (via pool-adapter's `tryQuarantineForMissingShadow`) — so the quarantine
|
|
82
|
+
* safety policy has a single source of truth (issue #2382 review, Finding B).
|
|
83
|
+
*
|
|
84
|
+
* 1. `wal-with-shadow` — the `.shadow` sidecar is PRESENT on disk. A
|
|
85
|
+
* "missing shadow" error alongside a present shadow means the open failed
|
|
86
|
+
* on path reachability or a lock (the #1811 non-ASCII path-garble on
|
|
87
|
+
* Windows), not a genuinely-missing shadow; quarantining would move a live
|
|
88
|
+
* WAL sitting next to its shadow — data loss.
|
|
89
|
+
* 2. `orphan-wal` — the orphan WAL is too large to safely discard
|
|
90
|
+
* (>TINY_ORPHAN_WAL_BYTES); preserve the uncheckpointed pages for explicit
|
|
91
|
+
* operator recovery.
|
|
92
|
+
*
|
|
93
|
+
* Throws `shadowSidecarRecoveryMessage` in either case. Returns silently only
|
|
94
|
+
* when the shadow is absent AND the WAL is absent or tiny — the states where
|
|
95
|
+
* the existing recovery path is safe to proceed. `mode` is a label used only in
|
|
96
|
+
* the warning text (e.g. 'read-only', 'writable', 'pool read-only recovery').
|
|
97
|
+
*/
|
|
98
|
+
export declare const guardWalQuarantine: (dbPath: string, mode: string, triggeringErr: unknown, logger: SidecarRecoveryLogger) => Promise<void>;
|
|
53
99
|
export declare function quarantineWalForMissingShadow(dbPath: string, options: {
|
|
54
100
|
logger: SidecarRecoveryLogger;
|
|
55
101
|
level?: 'debug' | 'info' | 'warn';
|
|
@@ -63,6 +109,132 @@ export declare function preflightLbugSidecars(dbPath: string, options: {
|
|
|
63
109
|
export declare function finalizeLbugSidecarsAfterClose(dbPath: string, options: {
|
|
64
110
|
logger: SidecarRecoveryLogger;
|
|
65
111
|
}): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Move the WAL/shadow sidecars aside before a dirty-flag recovery rebuild
|
|
114
|
+
* (#2409 defect 2).
|
|
115
|
+
*
|
|
116
|
+
* When `incrementalInProgress` forces a full rebuild, the previous run
|
|
117
|
+
* died mid-writeback — its WAL can be poisoned in a way that natively
|
|
118
|
+
* kills the process on replay. The recovery run used to open the DB
|
|
119
|
+
* BEFORE the rebuild wipe (the embedding-cache preservation open), replay
|
|
120
|
+
* the poisoned WAL, and die on the spot — so recovery never happened and
|
|
121
|
+
* only a manual rename-aside of the index dir escaped the loop. The
|
|
122
|
+
* rebuild discards every pending WAL byte anyway (the DB files are wiped),
|
|
123
|
+
* so parking the sidecars first costs nothing and makes every subsequent
|
|
124
|
+
* open replay-free.
|
|
125
|
+
*
|
|
126
|
+
* Renamed when possible, so the bytes stay available for post-mortem
|
|
127
|
+
* debugging — and, like {@link quarantineWalForMissingShadow}'s quarantine
|
|
128
|
+
* files, the parked copies are surfaced and removable by
|
|
129
|
+
* `gitnexus clean --lbug-sidecars` (tri-review 4669518496 P2-7; before
|
|
130
|
+
* that, this comment claimed a "same philosophy" parity while the
|
|
131
|
+
* dirty-recovery files were invisible to every cleanup surface). Real
|
|
132
|
+
* lifecycle: the destinations are FIXED names — no timestamp, see
|
|
133
|
+
* {@link listParkedDirtyRecoverySidecars} — so each new crash overwrites
|
|
134
|
+
* the previous parked copy, capping accumulation at one file per sidecar;
|
|
135
|
+
* remove them via `clean --lbug-sidecars` or manually once their
|
|
136
|
+
* post-mortem value has passed.
|
|
137
|
+
*
|
|
138
|
+
* Escalation ladder per suffix (this shipping review, FIX 1 — replacing
|
|
139
|
+
* the drop-shape design, whose park had ZERO retry while the wipe path
|
|
140
|
+
* retried the very same lock class):
|
|
141
|
+
*
|
|
142
|
+
* 1. `rename(from, to)` retried over the shared handle-release budget
|
|
143
|
+
* (HANDLE_RELEASE_PROBE_ATTEMPTS × linear HANDLE_RELEASE_PROBE_DELAY_MS,
|
|
144
|
+
* lbug-config.ts) — a transient AV/handle-lag EBUSY must not cost the
|
|
145
|
+
* run anything.
|
|
146
|
+
* 2. Structural confirm probe: a bare "does `to` exist?" check cannot
|
|
147
|
+
* discriminate a Windows rename-onto-existing collision from a locked
|
|
148
|
+
* source that happens to have a leftover parked copy. Renaming the
|
|
149
|
+
* source to the collision-free `${to}.next` can — success proves the
|
|
150
|
+
* failure was the collision, so the stale copy is replaced (newest
|
|
151
|
+
* forensics win). The crash window between the `rm(to)` and the final
|
|
152
|
+
* promote rename strands the bytes at `.next` — acceptable: `.next`
|
|
153
|
+
* residues are enumerated by the dirty-recovery lister and removed by
|
|
154
|
+
* `clean --lbug-sidecars` (FIX 5). Never pre-delete the previous
|
|
155
|
+
* crash's parked copy on the bet that a rename will then succeed
|
|
156
|
+
* (tri-review 4669518496 P2-3: the old rm-first shape destroyed the
|
|
157
|
+
* prior forensics exactly when the source was locked and nothing
|
|
158
|
+
* replaced them).
|
|
159
|
+
* 3. rm-fallback: the source itself is locked for RENAME, but Windows
|
|
160
|
+
* lets some holders' files be unlink-marked — retry
|
|
161
|
+
* `rm(from, {force:true})` over the same budget and require the file
|
|
162
|
+
* verifiably GONE. Success eliminates the replay risk at the cost of
|
|
163
|
+
* the post-mortem forensics (logged exactly so).
|
|
164
|
+
* 4. Report in `failed` with the corrected lock guidance — the caller
|
|
165
|
+
* must abort (run-analyze throws a LbugWipeError in seconds instead
|
|
166
|
+
* of running the whole pipeline and dying at the wipe on the same
|
|
167
|
+
* handle).
|
|
168
|
+
*
|
|
169
|
+
* Per-suffix isolation: a `.wal` failure never skips the `.shadow`
|
|
170
|
+
* attempt.
|
|
171
|
+
*
|
|
172
|
+
* INVARIANT: after this function returns, either no original sidecar
|
|
173
|
+
* remains adjacent to the DB — every entry is in `moved` or `removed`, so
|
|
174
|
+
* every subsequent open this run performs is replay-free — or the entry is
|
|
175
|
+
* in `failed` and the caller MUST abort before any DB open.
|
|
176
|
+
*
|
|
177
|
+
* @returns `moved` — destination paths now holding the parked bytes;
|
|
178
|
+
* `removed` — source sidecars whose bytes are GONE (forensics lost, replay
|
|
179
|
+
* risk eliminated); `failed` — source sidecars still in place: a
|
|
180
|
+
* possibly-poisoned sidecar sits next to the DB and any pre-wipe open
|
|
181
|
+
* would replay it and die (there is no "wipe it in place" fallback).
|
|
182
|
+
*/
|
|
183
|
+
export declare function quarantineSidecarsForDirtyRecovery(dbPath: string, log: (message: string) => void): Promise<{
|
|
184
|
+
moved: string[];
|
|
185
|
+
removed: string[];
|
|
186
|
+
failed: string[];
|
|
187
|
+
}>;
|
|
66
188
|
export declare function listQuarantinedMissingShadowWals(dbPath: string): Promise<string[]>;
|
|
189
|
+
/**
|
|
190
|
+
* Delete the missing-shadow WAL quarantines for `dbPath` and return the
|
|
191
|
+
* deleted paths. Locked files are skipped, not thrown (FIX 5) — user-facing
|
|
192
|
+
* surfaces should call {@link cleanParkedLbugSidecars}, which also REPORTS
|
|
193
|
+
* the skipped files.
|
|
194
|
+
*/
|
|
67
195
|
export declare function cleanQuarantinedMissingShadowWals(dbPath: string): Promise<string[]>;
|
|
196
|
+
/**
|
|
197
|
+
* List the `.dirty-recovery` sidecars parked beside `dbPath` by
|
|
198
|
+
* {@link quarantineSidecarsForDirtyRecovery}, so `gitnexus clean
|
|
199
|
+
* --lbug-sidecars` can surface them next to the missing-shadow quarantines
|
|
200
|
+
* (tri-review 4669518496 P2-7 — they were previously invisible to every
|
|
201
|
+
* cleanup surface). Only fixed names can exist (see
|
|
202
|
+
* {@link dirtyRecoveryParkedNames}: `<dbPath>.wal.dirty-recovery`,
|
|
203
|
+
* `<dbPath>.shadow.dirty-recovery`, and their `.next` probe residues from a
|
|
204
|
+
* double park failure — enumerated since FIX 5 of this shipping review; the
|
|
205
|
+
* docs used to say "remove manually" while no surface even listed them), so
|
|
206
|
+
* this stats them directly instead of prefix-scanning the directory the way
|
|
207
|
+
* the timestamped missing-shadow lister must.
|
|
208
|
+
*
|
|
209
|
+
* Returns existing parked files as sorted absolute paths. Branch-scoped
|
|
210
|
+
* index slots (`branches/<slug>/`) are outside `clean.ts`'s flat-path
|
|
211
|
+
* resolution — the same documented limitation as the missing-shadow pair.
|
|
212
|
+
*/
|
|
213
|
+
export declare function listParkedDirtyRecoverySidecars(dbPath: string): Promise<string[]>;
|
|
214
|
+
/**
|
|
215
|
+
* Delete the `.dirty-recovery` parked sidecars for `dbPath` and return the
|
|
216
|
+
* deleted paths. Sibling of {@link cleanQuarantinedMissingShadowWals}; same
|
|
217
|
+
* skip-not-throw policy (FIX 5) — user-facing surfaces should call
|
|
218
|
+
* {@link cleanParkedLbugSidecars}, which also reports locked files.
|
|
219
|
+
*/
|
|
220
|
+
export declare function cleanParkedDirtyRecoverySidecars(dbPath: string): Promise<string[]>;
|
|
221
|
+
/**
|
|
222
|
+
* Aggregate roster of every parked/quarantined sidecar family beside
|
|
223
|
+
* `dbPath` (this shipping review, FIX 5): the timestamped missing-shadow
|
|
224
|
+
* WAL quarantines plus the fixed-name dirty-recovery parks (`.next`
|
|
225
|
+
* residues included). Single roster authority for `clean --lbug-sidecars`
|
|
226
|
+
* — the command previously concatenated the families inline in two places,
|
|
227
|
+
* which is how the `.next` residue stayed invisible.
|
|
228
|
+
*/
|
|
229
|
+
export declare function listParkedLbugSidecars(dbPath: string): Promise<string[]>;
|
|
230
|
+
/**
|
|
231
|
+
* Delete every file {@link listParkedLbugSidecars} enumerates. Per-file
|
|
232
|
+
* error policy via {@link unlinkParkedFiles}: ENOENT skipped silently,
|
|
233
|
+
* locked files collected into `failed` while the rest are still deleted —
|
|
234
|
+
* a locked parked file must not crash the whole clean mid-command.
|
|
235
|
+
*/
|
|
236
|
+
export declare function cleanParkedLbugSidecars(dbPath: string): Promise<{
|
|
237
|
+
deleted: string[];
|
|
238
|
+
failed: string[];
|
|
239
|
+
}>;
|
|
68
240
|
export declare const _resetSidecarRecoveryWarningsForTest: () => void;
|