gitnexus 1.6.4-rc.2 → 1.6.4-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 +35 -0
- package/dist/_shared/index.d.ts +1 -1
- 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/scope-resolution/finalize-algorithm.d.ts +22 -14
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +298 -37
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +23 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +36 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +47 -3
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.js +10 -2
- package/dist/_shared/scope-resolution/types.js.map +1 -1
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +35 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +31 -0
- package/dist/cli/index.js +13 -0
- package/dist/cli/setup.js +2 -2
- package/dist/core/embeddings/config.d.ts +2 -0
- package/dist/core/embeddings/config.js +36 -0
- package/dist/core/embeddings/embedder.js +11 -6
- package/dist/core/embeddings/embedding-pipeline.d.ts +7 -1
- package/dist/core/embeddings/embedding-pipeline.js +93 -29
- package/dist/core/embeddings/exact-search.d.ts +15 -0
- package/dist/core/embeddings/exact-search.js +27 -0
- package/dist/core/embeddings/types.d.ts +4 -0
- package/dist/core/embeddings/types.js +2 -0
- package/dist/core/group/config-parser.js +2 -0
- package/dist/core/group/matching.d.ts +3 -3
- package/dist/core/group/matching.js +46 -6
- package/dist/core/group/storage.js +2 -0
- package/dist/core/group/sync.js +1 -1
- package/dist/core/group/types.d.ts +18 -0
- package/dist/core/ingestion/call-processor.d.ts +3 -3
- package/dist/core/ingestion/call-processor.js +58 -65
- package/dist/core/ingestion/constants.d.ts +4 -3
- package/dist/core/ingestion/constants.js +8 -3
- package/dist/core/ingestion/finalize-orchestrator.js +6 -3
- package/dist/core/ingestion/heritage-processor.js +2 -2
- package/dist/core/ingestion/import-processor.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp/captures.js +4 -1
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
- package/dist/core/ingestion/languages/python/captures.js +9 -1
- 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/simple-hooks.d.ts +3 -1
- package/dist/core/ingestion/languages/python/simple-hooks.js +8 -0
- package/dist/core/ingestion/languages/python.js +28 -1
- package/dist/core/ingestion/languages/swift.js +14 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
- package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
- package/dist/core/ingestion/languages/typescript/arity.js +54 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
- package/dist/core/ingestion/languages/typescript/captures.js +451 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
- package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
- package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
- package/dist/core/ingestion/languages/typescript/index.js +94 -0
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +77 -0
- package/dist/core/ingestion/languages/typescript/query.js +778 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
- package/dist/core/ingestion/languages/typescript.js +19 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +3 -4
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
- package/dist/core/ingestion/parsing-processor.js +20 -9
- package/dist/core/ingestion/pipeline-phases/processes.js +9 -4
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +10 -4
- package/dist/core/ingestion/registry-primary-flag.d.ts +3 -1
- package/dist/core/ingestion/registry-primary-flag.js +4 -1
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +5 -2
- package/dist/core/ingestion/scope-extractor-bridge.js +7 -2
- package/dist/core/ingestion/scope-extractor.js +19 -18
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +73 -11
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +48 -10
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +283 -14
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +23 -2
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +109 -37
- package/dist/core/ingestion/scope-resolution/passes/mro.js +3 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +13 -5
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +21 -5
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
- package/dist/core/ingestion/type-extractors/swift.js +7 -4
- package/dist/core/ingestion/utils/ast-helpers.d.ts +2 -0
- package/dist/core/ingestion/utils/ast-helpers.js +12 -0
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +14 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +15 -9
- package/dist/core/ingestion/workers/worker-pool.d.ts +11 -4
- package/dist/core/ingestion/workers/worker-pool.js +244 -48
- package/dist/core/lbug/extension-loader.d.ts +86 -0
- package/dist/core/lbug/extension-loader.js +184 -0
- package/dist/core/lbug/lbug-adapter.d.ts +18 -17
- package/dist/core/lbug/lbug-adapter.js +45 -73
- package/dist/core/lbug/pool-adapter.js +10 -28
- package/dist/core/platform/capabilities.d.ts +24 -0
- package/dist/core/platform/capabilities.js +54 -0
- package/dist/core/run-analyze.js +36 -9
- package/dist/core/search/bm25-index.d.ts +0 -17
- package/dist/core/search/bm25-index.js +10 -118
- package/dist/core/search/fts-indexes.d.ts +1 -0
- package/dist/core/search/fts-indexes.js +7 -0
- package/dist/core/search/fts-schema.d.ts +6 -0
- package/dist/core/search/fts-schema.js +7 -0
- package/dist/mcp/core/embedder.js +11 -4
- package/dist/mcp/local/local-backend.js +50 -15
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +113 -0
- package/hooks/claude/gitnexus-hook.cjs +11 -1
- package/package.json +6 -5
- package/scripts/build-tree-sitter-dart.cjs +42 -0
- package/scripts/build-tree-sitter-proto.cjs +1 -1
- package/scripts/build.js +22 -2
- package/scripts/install-duckdb-extension.mjs +37 -0
- package/vendor/tree-sitter-dart/README.md +18 -0
- package/vendor/tree-sitter-dart/binding.gyp +31 -0
- package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-dart/grammar.js +2895 -0
- package/vendor/tree-sitter-dart/package.json +18 -0
- package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
- package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
- package/vendor/tree-sitter-dart/queries/test.scm +1 -0
- package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
- package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
- package/vendor/tree-sitter-dart/src/parser.c +196127 -0
- package/vendor/tree-sitter-dart/src/scanner.c +130 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
- package/vendor/tree-sitter-swift/LICENSE +21 -0
- package/vendor/tree-sitter-swift/README.md +139 -0
- package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-swift/package.json +28 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
- package/web/assets/agent-DaprsFSX.js +597 -0
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-DEdGaPg2.js +1 -0
- package/web/assets/architectureDiagram-EMZXCZ2Q-Domyk_gO.js +36 -0
- package/web/assets/blockDiagram-IGV67L2C-B_2kD7tM.js +132 -0
- package/web/assets/c4Diagram-DFAF54RM-BhJJW8Gg.js +10 -0
- package/web/assets/chunk-3GS5O3IE-jlWIjPsl.js +231 -0
- package/web/assets/chunk-3YCYZ6SJ-Blq_IzZs.js +1 -0
- package/web/assets/chunk-6NTNNK5N-DyPc58pp.js +1 -0
- package/web/assets/chunk-7RZVMHOQ-BdIU-RGO.js +321 -0
- package/web/assets/chunk-A34GCYZU-BI2i_LdU.js +1 -0
- package/web/assets/chunk-AEOMTBSW-D7qjBMHW.js +1 -0
- package/web/assets/chunk-CilyBKbf.js +1 -0
- package/web/assets/chunk-DJ7UZH7F-i11ywiBl.js +1 -0
- package/web/assets/chunk-DKKBVRCY-1SffGI1N.js +4 -0
- package/web/assets/chunk-DU5LTGQ6-DaPeiwD5.js +1 -0
- package/web/assets/chunk-FXACKDTF-uhhi2PC2.js +159 -0
- package/web/assets/chunk-H3VCZNTA-IchcISDt.js +1 -0
- package/web/assets/chunk-HN6EAY2L-D7ZFMNrB.js +1 -0
- package/web/assets/chunk-KSICW3F5-C2tZmXwv.js +15 -0
- package/web/assets/chunk-O5ABG6QK-Bt-Km84H.js +1 -0
- package/web/assets/chunk-PK6DOVAG-ChlWY0BQ.js +206 -0
- package/web/assets/chunk-RNJOYNJ4-B724K7cW.js +1 -0
- package/web/assets/chunk-RWUO3TPN-DYn1XriD.js +1 -0
- package/web/assets/chunk-TBF5ZNIQ-DKtDz6ae.js +1 -0
- package/web/assets/chunk-TU3PZOEN-DE5Qhc0N.js +1 -0
- package/web/assets/chunk-TYMNRAUI-g1h33cq-.js +1 -0
- package/web/assets/chunk-VELTKBKT-C9dVN39o.js +1 -0
- package/web/assets/chunk-W7ZLLLMY-Du-Hb9yb.js +1 -0
- package/web/assets/chunk-WSB5WSVC-B123clsZ.js +1 -0
- package/web/assets/chunk-XGPFEOL4-BR7Eue38.js +1 -0
- package/web/assets/classDiagram-PPOCWD7C-BglfKSs_.js +1 -0
- package/web/assets/classDiagram-v2-23LJLIIU-BSzTM28O.js +1 -0
- package/web/assets/context-builder-CqQNhRj1.js +15 -0
- package/web/assets/cose-bilkent-PNC4W37J-DCfErU-A.js +1 -0
- package/web/assets/dagre-E77IOHMT-tDRRhDoN.js +4 -0
- package/web/assets/diagram-H7BISOXX-CUVHlmAh.js +43 -0
- package/web/assets/diagram-JC5VWROH-BoyOxulB.js +24 -0
- package/web/assets/diagram-LXUTUG65-osr9hb7N.js +10 -0
- package/web/assets/diagram-WEHSV5V5-d8nUqS39.js +24 -0
- package/web/assets/erDiagram-GCSMX5X6-b-IwOhPS.js +85 -0
- package/web/assets/flowDiagram-OTCZ4VVT-Ott2Q0AP.js +162 -0
- package/web/assets/ganttDiagram-MUNLMDZQ-BYtgN_5s.js +292 -0
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-CFyBIGZq.js +1 -0
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CsVD2gn4.js +106 -0
- package/web/assets/index-BleGLU8S.css +2 -0
- package/web/assets/index-C_xK08EW.js +885 -0
- package/web/assets/info-OMHHGYJF-BF2H5H6G-yjAxKEzh.js +1 -0
- package/web/assets/infoDiagram-MN7RKWGX-DXK0Unn5.js +2 -0
- package/web/assets/ishikawaDiagram-YMYX4NHK-CXsnC2FA.js +70 -0
- package/web/assets/journeyDiagram-SO5T7YLQ-BzZ07B-X.js +139 -0
- package/web/assets/kanban-definition-LJHFXRCJ-C6_EpAd9.js +89 -0
- package/web/assets/katex-GD7MH7QM-CJiOjBBJ.js +261 -0
- package/web/assets/mindmap-definition-2EUWGEK5-CCYGWZ1m.js +96 -0
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-B8k4E3IT.js +1 -0
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-DdvfY118.js +1 -0
- package/web/assets/pieDiagram-3IATQBI2-RyvRlQb4.js +30 -0
- package/web/assets/quadrantDiagram-E256RVCF-Bfb6sxCx.js +7 -0
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-1EEDC_yU.js +1 -0
- package/web/assets/requirementDiagram-M5DCFWZL-DjvHDyvN.js +84 -0
- package/web/assets/sankeyDiagram-L3NBLAOT-CBCbbl8s.js +10 -0
- package/web/assets/sequenceDiagram-ZOUHS735-BscU8TUR.js +157 -0
- package/web/assets/stateDiagram-MLPALWAM-CJusEK2D.js +1 -0
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-DImJ3PXD.js +1 -0
- package/web/assets/timeline-definition-5SPVSISX-DigPA1X8.js +120 -0
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-CzPDt3aG.js +1 -0
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-B9Iqiorr.js +1 -0
- package/web/assets/vennDiagram-IE5QUKF5-C91UkZIf.js +34 -0
- package/web/assets/wardley-RL74JXVD-BCRCBASE-x42Qw7hp.js +1 -0
- package/web/assets/wardleyDiagram-XU3VSMPF-DloBhI0U.js +20 -0
- package/web/assets/xychartDiagram-ZHJ5623Y-BGWJvgwI.js +7 -0
- package/web/index.html +21 -0
- package/scripts/patch-tree-sitter-swift.cjs +0 -78
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
const DEFAULT_EXTENSION_INSTALL_TIMEOUT_MS = 15_000;
|
|
4
|
+
const EXTENSION_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
5
|
+
const alreadyAvailable = (message) => message.includes('already loaded') ||
|
|
6
|
+
message.includes('already installed') ||
|
|
7
|
+
message.includes('already exists');
|
|
8
|
+
const resolvePolicyFromEnv = () => {
|
|
9
|
+
const raw = process.env.GITNEXUS_LBUG_EXTENSION_INSTALL;
|
|
10
|
+
if (raw === 'load-only' || raw === 'never' || raw === 'auto')
|
|
11
|
+
return raw;
|
|
12
|
+
return 'auto';
|
|
13
|
+
};
|
|
14
|
+
export const getExtensionInstallTimeoutMs = () => {
|
|
15
|
+
const raw = process.env.GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS;
|
|
16
|
+
const parsed = raw ? Number(raw) : NaN;
|
|
17
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_EXTENSION_INSTALL_TIMEOUT_MS;
|
|
18
|
+
};
|
|
19
|
+
export const getExtensionInstallChildProcessArgs = (extensionName) => {
|
|
20
|
+
const childScript = new URL('../../../scripts/install-duckdb-extension.mjs', import.meta.url);
|
|
21
|
+
return [fileURLToPath(childScript), extensionName];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Run `INSTALL <extension>` in a short-lived child Node process so the parent
|
|
25
|
+
* event loop is never blocked by DuckDB's synchronous network call.
|
|
26
|
+
*
|
|
27
|
+
* The child opens its own scratch LadybugDB, executes the install, and exits.
|
|
28
|
+
* If the child exceeds `timeoutMs` the parent kills it with SIGKILL and
|
|
29
|
+
* resolves with `timedOut: true`.
|
|
30
|
+
*/
|
|
31
|
+
export const installDuckDbExtensionOutOfProcess = async (extensionName, timeoutMs = getExtensionInstallTimeoutMs()) => {
|
|
32
|
+
if (!EXTENSION_NAME_PATTERN.test(extensionName)) {
|
|
33
|
+
throw new Error(`Invalid DuckDB extension name: ${extensionName}`);
|
|
34
|
+
}
|
|
35
|
+
return await new Promise((resolve) => {
|
|
36
|
+
const child = spawn(process.execPath, getExtensionInstallChildProcessArgs(extensionName), {
|
|
37
|
+
env: {
|
|
38
|
+
...process.env,
|
|
39
|
+
GITNEXUS_LBUG_EXTENSION_NAME: extensionName,
|
|
40
|
+
},
|
|
41
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
42
|
+
windowsHide: true,
|
|
43
|
+
});
|
|
44
|
+
let stderr = '';
|
|
45
|
+
child.stderr?.setEncoding('utf8');
|
|
46
|
+
child.stderr?.on('data', (chunk) => {
|
|
47
|
+
stderr = (stderr + chunk).slice(-4000);
|
|
48
|
+
});
|
|
49
|
+
let settled = false;
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
if (settled)
|
|
52
|
+
return;
|
|
53
|
+
settled = true;
|
|
54
|
+
child.kill('SIGKILL');
|
|
55
|
+
resolve({
|
|
56
|
+
success: false,
|
|
57
|
+
timedOut: true,
|
|
58
|
+
message: `INSTALL ${extensionName} timed out after ${timeoutMs}ms`,
|
|
59
|
+
});
|
|
60
|
+
}, timeoutMs);
|
|
61
|
+
child.on('error', (err) => {
|
|
62
|
+
if (settled)
|
|
63
|
+
return;
|
|
64
|
+
settled = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
resolve({ success: false, timedOut: false, message: err.message });
|
|
67
|
+
});
|
|
68
|
+
child.on('exit', (code, signal) => {
|
|
69
|
+
if (settled)
|
|
70
|
+
return;
|
|
71
|
+
settled = true;
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
resolve({
|
|
74
|
+
success: code === 0,
|
|
75
|
+
timedOut: false,
|
|
76
|
+
message: code === 0
|
|
77
|
+
? `INSTALL ${extensionName} completed`
|
|
78
|
+
: `INSTALL ${extensionName} failed with ${signal ?? `exit code ${code}`}${stderr ? `: ${stderr.trim()}` : ''}`,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Centralized lifecycle manager for optional LadybugDB extensions.
|
|
85
|
+
*
|
|
86
|
+
* Always tries `LOAD EXTENSION <name>` first — it is per-connection,
|
|
87
|
+
* idempotent, and never touches the network. If `LOAD` fails and the active
|
|
88
|
+
* policy permits, the manager runs a single bounded out-of-process `INSTALL`
|
|
89
|
+
* attempt per process and retries `LOAD`. Capability outcomes are cached so
|
|
90
|
+
* unavailable extensions degrade search features without ever blocking
|
|
91
|
+
* subsequent analyze or query calls.
|
|
92
|
+
*
|
|
93
|
+
* Policy precedence (most specific wins):
|
|
94
|
+
* per-call `opts.policy` → constructor `options.policy` → env → `auto`
|
|
95
|
+
*/
|
|
96
|
+
export class ExtensionManager {
|
|
97
|
+
options;
|
|
98
|
+
capabilities = new Map();
|
|
99
|
+
installAttempted = new Map();
|
|
100
|
+
warnedKeys = new Set();
|
|
101
|
+
constructor(options = {}) {
|
|
102
|
+
this.options = options;
|
|
103
|
+
}
|
|
104
|
+
/** Reset cached capability and install state. Test-only. */
|
|
105
|
+
reset() {
|
|
106
|
+
this.capabilities.clear();
|
|
107
|
+
this.installAttempted.clear();
|
|
108
|
+
this.warnedKeys.clear();
|
|
109
|
+
}
|
|
110
|
+
/** Snapshot of currently-known optional extension capabilities. */
|
|
111
|
+
getCapabilities() {
|
|
112
|
+
return Array.from(this.capabilities.values());
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Ensure an optional extension is loaded on the supplied connection.
|
|
116
|
+
*
|
|
117
|
+
* Returns `true` when the extension is usable on `query`, `false` when it
|
|
118
|
+
* is unavailable. Never throws on install failure — analyze and query
|
|
119
|
+
* paths are expected to degrade gracefully.
|
|
120
|
+
*/
|
|
121
|
+
async ensure(query, name, label, opts = {}) {
|
|
122
|
+
if (!EXTENSION_NAME_PATTERN.test(name)) {
|
|
123
|
+
throw new Error(`Invalid DuckDB extension name: ${name}`);
|
|
124
|
+
}
|
|
125
|
+
const policy = opts.policy ?? this.options.policy ?? resolvePolicyFromEnv();
|
|
126
|
+
const timeoutMs = opts.installTimeoutMs ?? this.options.installTimeoutMs ?? getExtensionInstallTimeoutMs();
|
|
127
|
+
const warn = this.options.warn ?? console.warn;
|
|
128
|
+
if (policy === 'never') {
|
|
129
|
+
this.markUnavailable(name, label, 'extension install policy is "never"', warn);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (await this.tryLoad(query, name)) {
|
|
133
|
+
this.markLoaded(name);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
if (policy === 'load-only') {
|
|
137
|
+
this.markUnavailable(name, label, 'load-only policy: extension not pre-installed', warn);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
let install = this.installAttempted.get(name);
|
|
141
|
+
if (!install) {
|
|
142
|
+
const installFn = this.options.installExtension ?? installDuckDbExtensionOutOfProcess;
|
|
143
|
+
install = await installFn(name, timeoutMs);
|
|
144
|
+
this.installAttempted.set(name, install);
|
|
145
|
+
}
|
|
146
|
+
if (!install.success) {
|
|
147
|
+
this.markUnavailable(name, label, install.message, warn);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (await this.tryLoad(query, name)) {
|
|
151
|
+
this.markLoaded(name);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
this.markUnavailable(name, label, `LOAD ${name} failed after successful INSTALL`, warn);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
async tryLoad(query, name) {
|
|
158
|
+
try {
|
|
159
|
+
await query(`LOAD EXTENSION ${name}`);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
164
|
+
return alreadyAvailable(msg);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
markLoaded(name) {
|
|
168
|
+
this.capabilities.set(name, { name, loaded: true });
|
|
169
|
+
}
|
|
170
|
+
markUnavailable(name, label, reason, warn) {
|
|
171
|
+
this.capabilities.set(name, { name, loaded: false, reason });
|
|
172
|
+
const key = `${name}:${reason}`;
|
|
173
|
+
if (this.warnedKeys.has(key))
|
|
174
|
+
return;
|
|
175
|
+
this.warnedKeys.add(key);
|
|
176
|
+
warn(`GitNexus: ${label} extension unavailable; continuing without ${label} features. ${reason}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Process-wide singleton shared by core and pool adapters. */
|
|
180
|
+
export const extensionManager = new ExtensionManager();
|
|
181
|
+
/** Snapshot of which optional DuckDB extensions are loaded in this process. */
|
|
182
|
+
export const getExtensionCapabilities = () => extensionManager.getCapabilities();
|
|
183
|
+
/** Test-only: clear the singleton's cached capability and install state. */
|
|
184
|
+
export const resetExtensionState = () => extensionManager.reset();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import lbug from '@ladybugdb/core';
|
|
2
2
|
import { KnowledgeGraph } from '../graph/types.js';
|
|
3
3
|
import type { CachedEmbedding } from '../embeddings/types.js';
|
|
4
|
+
import { type ExtensionEnsureOptions } from './extension-loader.js';
|
|
4
5
|
/** Factory for creating WriteStreams — injectable for testing. */
|
|
5
6
|
export type WriteStreamFactory = (filePath: string) => import('fs').WriteStream;
|
|
6
7
|
/** Result of splitting the relationship CSV into per-label-pair files. */
|
|
@@ -122,23 +123,24 @@ export declare const deleteNodesForFile: (filePath: string, dbPath?: string) =>
|
|
|
122
123
|
}>;
|
|
123
124
|
export declare const getEmbeddingTableName: () => string;
|
|
124
125
|
/**
|
|
125
|
-
* Load the FTS extension
|
|
126
|
+
* Load the FTS extension on the supplied connection (or the singleton
|
|
127
|
+
* writable connection when none is given).
|
|
126
128
|
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* hasn't been cached yet.
|
|
129
|
+
* Delegates to the shared `ExtensionManager` so install policy (auto /
|
|
130
|
+
* load-only / never), out-of-process bounded INSTALL, and capability
|
|
131
|
+
* caching are owned in one place. The module-level `ftsLoaded` flag is
|
|
132
|
+
* kept purely as a per-call short-circuit on the singleton writable
|
|
133
|
+
* connection so repeated callers (e.g. createFTSIndex) avoid an extra
|
|
134
|
+
* `LOAD` round-trip per invocation. Pool adapter callers pass
|
|
135
|
+
* `{ policy: 'load-only' }` so query paths never block on a network install.
|
|
135
136
|
*/
|
|
136
|
-
export declare const loadFTSExtension: (targetConn?: lbug.Connection) => Promise<boolean>;
|
|
137
|
+
export declare const loadFTSExtension: (targetConn?: lbug.Connection, opts?: ExtensionEnsureOptions) => Promise<boolean>;
|
|
137
138
|
/**
|
|
138
|
-
* Load the VECTOR extension (
|
|
139
|
-
*
|
|
139
|
+
* Load the VECTOR extension on the supplied connection (or the singleton
|
|
140
|
+
* writable connection when none is given). Returns false when VECTOR is
|
|
141
|
+
* unavailable so semantic search can fall back to exact scan.
|
|
140
142
|
*/
|
|
141
|
-
export declare const loadVectorExtension: () => Promise<
|
|
143
|
+
export declare const loadVectorExtension: (targetConn?: lbug.Connection, opts?: ExtensionEnsureOptions) => Promise<boolean>;
|
|
142
144
|
/**
|
|
143
145
|
* Create a full-text search index on a table
|
|
144
146
|
* @param tableName - The node table name (e.g., 'File', 'CodeSymbol')
|
|
@@ -150,10 +152,9 @@ export declare const createFTSIndex: (tableName: string, indexName: string, prop
|
|
|
150
152
|
/**
|
|
151
153
|
* Lazy-create an FTS index, caching the fact in-process.
|
|
152
154
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* the
|
|
156
|
-
* where it's amortised across many lookups.
|
|
155
|
+
* Kept for writable maintenance paths that need to lazily materialize an
|
|
156
|
+
* index. Read-only query paths must not call this; production analysis owns
|
|
157
|
+
* creating the configured search indexes before the database is served.
|
|
157
158
|
*
|
|
158
159
|
* Safe to call repeatedly — the in-process Set guarantees only the first
|
|
159
160
|
* call hits LadybugDB. `closeLbug` clears the cache so re-init starts fresh.
|
|
@@ -7,6 +7,8 @@ import path from 'path';
|
|
|
7
7
|
import lbug from '@ladybugdb/core';
|
|
8
8
|
import { NODE_TABLES, REL_TABLE_NAME, SCHEMA_QUERIES, EMBEDDING_TABLE_NAME, STALE_HASH_SENTINEL, } from './schema.js';
|
|
9
9
|
import { streamAllCSVsToDisk } from './csv-generator.js';
|
|
10
|
+
import { extensionManager } from './extension-loader.js';
|
|
11
|
+
import { isVectorExtensionSupportedByPlatform } from '../platform/capabilities.js';
|
|
10
12
|
/**
|
|
11
13
|
* Split a relationship CSV into per-label-pair files on disk.
|
|
12
14
|
*
|
|
@@ -112,11 +114,9 @@ let ftsLoaded = false;
|
|
|
112
114
|
let vectorExtensionLoaded = false;
|
|
113
115
|
/**
|
|
114
116
|
* In-process cache of FTS indexes that have been ensured against the current
|
|
115
|
-
* connection. Prevents repeated `CALL CREATE_FTS_INDEX` round-trips
|
|
116
|
-
*
|
|
117
|
-
* `
|
|
118
|
-
* index already exists on disk), subsequent calls are a Set lookup. Cleared
|
|
119
|
-
* by `closeLbug` so a re-init starts fresh.
|
|
117
|
+
* writable connection. Prevents repeated `CALL CREATE_FTS_INDEX` round-trips
|
|
118
|
+
* for callers that explicitly opt into `ensureFTSIndex`. Cleared by
|
|
119
|
+
* `closeLbug` so a re-init starts fresh.
|
|
120
120
|
*
|
|
121
121
|
* Key format: `${tableName}:${indexName}`.
|
|
122
122
|
*/
|
|
@@ -289,8 +289,9 @@ const doInitLbug = async (dbPath) => {
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
-
//
|
|
293
|
-
|
|
292
|
+
// FTS powers baseline search, so initialize it with the core DB. VECTOR is
|
|
293
|
+
// only required for semantic embeddings and is probed lazily there.
|
|
294
|
+
await loadFTSExtension();
|
|
294
295
|
currentDbPath = dbPath;
|
|
295
296
|
return { db, conn };
|
|
296
297
|
};
|
|
@@ -761,8 +762,9 @@ export const executeWithReusedStatement = async (cypher, paramsList) => {
|
|
|
761
762
|
}
|
|
762
763
|
}
|
|
763
764
|
catch (e) {
|
|
764
|
-
|
|
765
|
-
|
|
765
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
766
|
+
const queryPreview = cypher.replace(/\s+/g, ' ').slice(0, 120);
|
|
767
|
+
throw new Error(`Batch execution failed for rows ${i + 1}-${i + subBatch.length}: ${msg} (${queryPreview})`);
|
|
766
768
|
}
|
|
767
769
|
// Note: LadybugDB PreparedStatement doesn't require explicit close()
|
|
768
770
|
}
|
|
@@ -1025,18 +1027,18 @@ export const getEmbeddingTableName = () => EMBEDDING_TABLE_NAME;
|
|
|
1025
1027
|
// Full-Text Search (FTS) Functions
|
|
1026
1028
|
// ============================================================================
|
|
1027
1029
|
/**
|
|
1028
|
-
* Load the FTS extension
|
|
1029
|
-
*
|
|
1030
|
-
* Safe to call multiple times — when invoked without arguments, tracks loaded
|
|
1031
|
-
* state via module-level `ftsLoaded`. When invoked with an explicit
|
|
1032
|
-
* connection, loads on that connection and returns whether the load
|
|
1033
|
-
* succeeded — letting callers (e.g. the pool adapter) track their own state.
|
|
1030
|
+
* Load the FTS extension on the supplied connection (or the singleton
|
|
1031
|
+
* writable connection when none is given).
|
|
1034
1032
|
*
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1033
|
+
* Delegates to the shared `ExtensionManager` so install policy (auto /
|
|
1034
|
+
* load-only / never), out-of-process bounded INSTALL, and capability
|
|
1035
|
+
* caching are owned in one place. The module-level `ftsLoaded` flag is
|
|
1036
|
+
* kept purely as a per-call short-circuit on the singleton writable
|
|
1037
|
+
* connection so repeated callers (e.g. createFTSIndex) avoid an extra
|
|
1038
|
+
* `LOAD` round-trip per invocation. Pool adapter callers pass
|
|
1039
|
+
* `{ policy: 'load-only' }` so query paths never block on a network install.
|
|
1038
1040
|
*/
|
|
1039
|
-
export const loadFTSExtension = async (targetConn) => {
|
|
1041
|
+
export const loadFTSExtension = async (targetConn, opts = {}) => {
|
|
1040
1042
|
const useModuleState = targetConn === undefined;
|
|
1041
1043
|
if (useModuleState && ftsLoaded)
|
|
1042
1044
|
return true;
|
|
@@ -1044,61 +1046,30 @@ export const loadFTSExtension = async (targetConn) => {
|
|
|
1044
1046
|
if (!c) {
|
|
1045
1047
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1046
1048
|
}
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
};
|
|
1052
|
-
try {
|
|
1053
|
-
// Try loading locally first (no network required)
|
|
1054
|
-
await c.query('LOAD EXTENSION fts');
|
|
1055
|
-
return markLoaded();
|
|
1056
|
-
}
|
|
1057
|
-
catch {
|
|
1058
|
-
// Fall back to install + load (requires network)
|
|
1059
|
-
try {
|
|
1060
|
-
await c.query('INSTALL fts');
|
|
1061
|
-
await c.query('LOAD EXTENSION fts');
|
|
1062
|
-
return markLoaded();
|
|
1063
|
-
}
|
|
1064
|
-
catch (err) {
|
|
1065
|
-
const msg = err?.message || '';
|
|
1066
|
-
if (msg.includes('already loaded') ||
|
|
1067
|
-
msg.includes('already installed') ||
|
|
1068
|
-
msg.includes('already exists')) {
|
|
1069
|
-
return markLoaded();
|
|
1070
|
-
}
|
|
1071
|
-
console.error('GitNexus: FTS extension load failed:', msg);
|
|
1072
|
-
return false;
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1049
|
+
const loaded = await extensionManager.ensure((sql) => c.query(sql), 'fts', 'FTS', opts);
|
|
1050
|
+
if (loaded && useModuleState)
|
|
1051
|
+
ftsLoaded = true;
|
|
1052
|
+
return loaded;
|
|
1075
1053
|
};
|
|
1076
1054
|
/**
|
|
1077
|
-
* Load the VECTOR extension (
|
|
1078
|
-
*
|
|
1055
|
+
* Load the VECTOR extension on the supplied connection (or the singleton
|
|
1056
|
+
* writable connection when none is given). Returns false when VECTOR is
|
|
1057
|
+
* unavailable so semantic search can fall back to exact scan.
|
|
1079
1058
|
*/
|
|
1080
|
-
export const loadVectorExtension = async () => {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1059
|
+
export const loadVectorExtension = async (targetConn, opts = {}) => {
|
|
1060
|
+
const useModuleState = targetConn === undefined;
|
|
1061
|
+
if (useModuleState && vectorExtensionLoaded)
|
|
1062
|
+
return true;
|
|
1063
|
+
if (!isVectorExtensionSupportedByPlatform())
|
|
1064
|
+
return false;
|
|
1065
|
+
const c = targetConn ?? conn;
|
|
1066
|
+
if (!c) {
|
|
1084
1067
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1085
1068
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
await conn.query('LOAD EXTENSION VECTOR');
|
|
1069
|
+
const loaded = await extensionManager.ensure((sql) => c.query(sql), 'VECTOR', 'VECTOR', opts);
|
|
1070
|
+
if (loaded && useModuleState)
|
|
1089
1071
|
vectorExtensionLoaded = true;
|
|
1090
|
-
|
|
1091
|
-
catch (err) {
|
|
1092
|
-
const msg = err?.message || '';
|
|
1093
|
-
if (msg.includes('already loaded') ||
|
|
1094
|
-
msg.includes('already installed') ||
|
|
1095
|
-
msg.includes('already exists')) {
|
|
1096
|
-
vectorExtensionLoaded = true;
|
|
1097
|
-
}
|
|
1098
|
-
else {
|
|
1099
|
-
console.error('GitNexus: VECTOR extension load failed:', msg);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1072
|
+
return loaded;
|
|
1102
1073
|
};
|
|
1103
1074
|
/**
|
|
1104
1075
|
* Create a full-text search index on a table
|
|
@@ -1111,7 +1082,9 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
|
|
|
1111
1082
|
if (!conn) {
|
|
1112
1083
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1113
1084
|
}
|
|
1114
|
-
await loadFTSExtension()
|
|
1085
|
+
if (!(await loadFTSExtension())) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1115
1088
|
const propList = properties.map((p) => `'${p}'`).join(', ');
|
|
1116
1089
|
const query = `CALL CREATE_FTS_INDEX('${tableName}', '${indexName}', [${propList}], stemmer := '${stemmer}')`;
|
|
1117
1090
|
try {
|
|
@@ -1126,10 +1099,9 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
|
|
|
1126
1099
|
/**
|
|
1127
1100
|
* Lazy-create an FTS index, caching the fact in-process.
|
|
1128
1101
|
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1131
|
-
* the
|
|
1132
|
-
* where it's amortised across many lookups.
|
|
1102
|
+
* Kept for writable maintenance paths that need to lazily materialize an
|
|
1103
|
+
* index. Read-only query paths must not call this; production analysis owns
|
|
1104
|
+
* creating the configured search indexes before the database is served.
|
|
1133
1105
|
*
|
|
1134
1106
|
* Safe to call repeatedly — the in-process Set guarantees only the first
|
|
1135
1107
|
* call hits LadybugDB. `closeLbug` clears the cache so re-init starts fresh.
|
|
@@ -122,7 +122,6 @@ function closeOne(repoId) {
|
|
|
122
122
|
// for the same dbPath reuse it instead of hitting a file lock.
|
|
123
123
|
shared.refCount = 0;
|
|
124
124
|
shared.ftsLoaded = false;
|
|
125
|
-
shared.vectorLoaded = false;
|
|
126
125
|
}
|
|
127
126
|
else {
|
|
128
127
|
shared.db.close().catch(() => { });
|
|
@@ -248,7 +247,7 @@ async function doInitLbug(repoId, dbPath) {
|
|
|
248
247
|
false, // enableCompression (default)
|
|
249
248
|
true);
|
|
250
249
|
restoreStdout();
|
|
251
|
-
shared = { db, refCount: 0, ftsLoaded: false
|
|
250
|
+
shared = { db, refCount: 0, ftsLoaded: false };
|
|
252
251
|
dbCache.set(dbPath, shared);
|
|
253
252
|
break;
|
|
254
253
|
}
|
|
@@ -284,19 +283,11 @@ async function doInitLbug(repoId, dbPath) {
|
|
|
284
283
|
// Load FTS extension once per shared Database.
|
|
285
284
|
// Done BEFORE pool registration so no concurrent checkout can grab
|
|
286
285
|
// the connection while the async FTS load is in progress.
|
|
286
|
+
// policy: 'load-only' — the read pool must never trigger a network
|
|
287
|
+
// install; analyze owns extension installation. If LOAD fails, search
|
|
288
|
+
// features degrade gracefully and the user-facing query path proceeds.
|
|
287
289
|
if (!shared.ftsLoaded) {
|
|
288
|
-
shared.ftsLoaded = await loadFTSExtension(available[0]);
|
|
289
|
-
}
|
|
290
|
-
// Load VECTOR extension once per shared Database for semantic search support.
|
|
291
|
-
if (!shared.vectorLoaded) {
|
|
292
|
-
try {
|
|
293
|
-
await available[0].query('INSTALL VECTOR');
|
|
294
|
-
await available[0].query('LOAD EXTENSION VECTOR');
|
|
295
|
-
shared.vectorLoaded = true;
|
|
296
|
-
}
|
|
297
|
-
catch {
|
|
298
|
-
// VECTOR extension may not be available
|
|
299
|
-
}
|
|
290
|
+
shared.ftsLoaded = await loadFTSExtension(available[0], { policy: 'load-only' });
|
|
300
291
|
}
|
|
301
292
|
// Register pool entry only after all connections are pre-warmed and FTS is
|
|
302
293
|
// loaded. Concurrent executeQuery calls see either "not initialized"
|
|
@@ -335,7 +326,7 @@ export async function initLbugWithDb(repoId, existingDb, dbPath) {
|
|
|
335
326
|
// closeOne() respects the external flag and skips db.close().
|
|
336
327
|
let shared = dbCache.get(dbPath);
|
|
337
328
|
if (!shared) {
|
|
338
|
-
shared = { db: existingDb, refCount: 0, ftsLoaded: false,
|
|
329
|
+
shared = { db: existingDb, refCount: 0, ftsLoaded: false, external: true };
|
|
339
330
|
dbCache.set(dbPath, shared);
|
|
340
331
|
}
|
|
341
332
|
shared.refCount++;
|
|
@@ -349,20 +340,11 @@ export async function initLbugWithDb(repoId, existingDb, dbPath) {
|
|
|
349
340
|
finally {
|
|
350
341
|
preWarmActive = false;
|
|
351
342
|
}
|
|
352
|
-
// Load FTS extension if not already loaded on this Database
|
|
343
|
+
// Load FTS extension if not already loaded on this Database.
|
|
344
|
+
// policy: 'load-only' — same contract as initLbug above; the read pool
|
|
345
|
+
// must not block on a network install during query execution.
|
|
353
346
|
if (!shared.ftsLoaded) {
|
|
354
|
-
shared.ftsLoaded = await loadFTSExtension(available[0]);
|
|
355
|
-
}
|
|
356
|
-
// Load VECTOR extension for semantic search support
|
|
357
|
-
if (!shared.vectorLoaded) {
|
|
358
|
-
try {
|
|
359
|
-
await available[0].query('INSTALL VECTOR');
|
|
360
|
-
await available[0].query('LOAD EXTENSION VECTOR');
|
|
361
|
-
shared.vectorLoaded = true;
|
|
362
|
-
}
|
|
363
|
-
catch {
|
|
364
|
-
// VECTOR extension may not be available
|
|
365
|
-
}
|
|
347
|
+
shared.ftsLoaded = await loadFTSExtension(available[0], { policy: 'load-only' });
|
|
366
348
|
}
|
|
367
349
|
pool.set(repoId, {
|
|
368
350
|
db: existingDb,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type CapabilityStatus = 'available' | 'degraded' | 'unavailable';
|
|
2
|
+
export type SemanticSearchMode = 'vector-index' | 'exact-scan' | 'unavailable';
|
|
3
|
+
export interface RuntimeFingerprint {
|
|
4
|
+
platform: NodeJS.Platform;
|
|
5
|
+
arch: string;
|
|
6
|
+
node: string;
|
|
7
|
+
gitnexus: string;
|
|
8
|
+
ladybugdb?: string;
|
|
9
|
+
onnxruntime?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RuntimeCapabilities {
|
|
12
|
+
graph: CapabilityStatus;
|
|
13
|
+
fts: CapabilityStatus;
|
|
14
|
+
vector: CapabilityStatus;
|
|
15
|
+
semanticMode: SemanticSearchMode;
|
|
16
|
+
exactScanLimit: number;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const DEFAULT_EXACT_SCAN_LIMIT = 10000;
|
|
20
|
+
export declare const getExactScanLimit: () => number;
|
|
21
|
+
export declare const getRuntimeFingerprint: () => RuntimeFingerprint;
|
|
22
|
+
export declare const isVectorExtensionSupportedByPlatform: (platform?: NodeJS.Platform) => boolean;
|
|
23
|
+
export declare const getRuntimeCapabilities: () => RuntimeCapabilities;
|
|
24
|
+
export declare const defaultEmbeddingThreads: () => number;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const packageVersion = (name) => {
|
|
5
|
+
try {
|
|
6
|
+
return require(`${name}/package.json`).version;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const gitnexusVersion = () => {
|
|
13
|
+
try {
|
|
14
|
+
return require('../../../package.json').version;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 'unknown';
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const parsePositiveInt = (value, fallback) => {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return fallback;
|
|
23
|
+
const parsed = Number(value);
|
|
24
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
25
|
+
};
|
|
26
|
+
export const DEFAULT_EXACT_SCAN_LIMIT = 10_000;
|
|
27
|
+
export const getExactScanLimit = () => parsePositiveInt(process.env.GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT, DEFAULT_EXACT_SCAN_LIMIT);
|
|
28
|
+
export const getRuntimeFingerprint = () => ({
|
|
29
|
+
platform: process.platform,
|
|
30
|
+
arch: process.arch,
|
|
31
|
+
node: process.version,
|
|
32
|
+
gitnexus: gitnexusVersion(),
|
|
33
|
+
ladybugdb: packageVersion('@ladybugdb/core'),
|
|
34
|
+
onnxruntime: packageVersion('onnxruntime-node'),
|
|
35
|
+
});
|
|
36
|
+
export const isVectorExtensionSupportedByPlatform = (platform = process.platform) => platform !== 'win32';
|
|
37
|
+
export const getRuntimeCapabilities = () => {
|
|
38
|
+
const vector = isVectorExtensionSupportedByPlatform() ? 'available' : 'unavailable';
|
|
39
|
+
const exactScanLimit = getExactScanLimit();
|
|
40
|
+
return {
|
|
41
|
+
graph: 'available',
|
|
42
|
+
fts: 'available',
|
|
43
|
+
vector,
|
|
44
|
+
semanticMode: vector === 'available' ? 'vector-index' : 'exact-scan',
|
|
45
|
+
exactScanLimit,
|
|
46
|
+
reason: vector === 'unavailable'
|
|
47
|
+
? 'LadybugDB VECTOR is disabled on this platform; semantic search uses exact scan when embeddings exist.'
|
|
48
|
+
: undefined,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export const defaultEmbeddingThreads = () => {
|
|
52
|
+
const available = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
53
|
+
return Math.max(1, Math.min(4, Math.floor(available / 2) || 1));
|
|
54
|
+
};
|