@dikolab/kbdb 0.2.1 → 0.3.0
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 +59 -24
- package/dist/cli.cjs +462 -73
- package/dist/cli.cjs.map +4 -4
- package/dist/cli.mjs +454 -65
- package/dist/cli.mjs.map +4 -4
- package/dist/kbdb-worker.cjs +395 -60
- package/dist/kbdb-worker.cjs.map +4 -4
- package/dist/mod.cjs +18 -1
- package/dist/mod.cjs.map +2 -2
- package/dist/mod.mjs +18 -1
- package/dist/mod.mjs.map +2 -2
- package/dist/src/shared/cli/functions/dispatch-command.function.d.ts +3 -1
- package/dist/src/shared/cli/functions/install-default-skills.function.d.ts +12 -0
- package/dist/src/shared/cli/functions/run-check.function.d.ts +47 -5
- package/dist/src/shared/cli/functions/run-export.function.d.ts +34 -0
- package/dist/src/shared/cli/functions/run-gc.function.d.ts +6 -0
- package/dist/src/shared/cli/functions/run-learn.function.d.ts +35 -6
- package/dist/src/shared/cli/functions/run-recall.function.d.ts +45 -0
- package/dist/src/shared/cli/functions/run-search.function.d.ts +52 -15
- package/dist/src/shared/cli/index.d.ts +4 -0
- package/dist/src/shared/cli/typings/cli-options.interface.d.ts +13 -0
- package/dist/src/shared/cli/typings/learn-result.model.d.ts +19 -0
- package/dist/src/shared/cli/typings/search-display.model.d.ts +18 -0
- package/dist/src/shared/mcp/classes/mcp-server.class.d.ts +8 -0
- package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +43 -0
- package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
- package/dist/src/shared/wasm-codec/functions/decode-add-section-result.function.d.ts +48 -0
- package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +58 -0
- package/dist/src/shared/wasm-codec/functions/decode-section-records.function.d.ts +33 -0
- package/dist/src/shared/wasm-codec/functions/encode-add-section-params.function.d.ts +52 -0
- package/dist/src/shared/wasm-codec/index.d.ts +2 -0
- package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +16 -0
- package/dist/src/shared/worker-client/index.d.ts +1 -1
- package/dist/src/shared/worker-client/typings/add-section-result.model.d.ts +29 -15
- package/dist/src/shared/worker-client/typings/export-result.model.d.ts +14 -0
- package/dist/src/shared/worker-client/typings/gc-result.model.d.ts +6 -0
- package/dist/src/shared/worker-client/typings/search-result.model.d.ts +12 -0
- package/dist/src/shared/worker-client/typings/section.model.d.ts +16 -0
- package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +35 -0
- package/dist/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
- package/dist/wasm/fs-database/kbdb_fs_database.js +28 -1
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
- package/dist/wasm/kb-worker/kbdb_worker.d.ts +48 -1
- package/dist/wasm/kb-worker/kbdb_worker.js +60 -1
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
- package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
- package/dist/worker.mjs +396 -60
- package/dist/worker.mjs.map +4 -4
- package/docs/details/cli.md +102 -15
- package/docs/details/library-api.md +55 -6
- package/docs/details/mcp-server.md +8 -4
- package/docs/details/search-and-ranking.md +63 -5
- package/docs/details/storage.md +2 -0
- package/docs/goals/cli.md +255 -14
- package/docs/goals/database.md +244 -17
- package/docs/goals/document.md +1 -1
- package/docs/goals/mcp.md +83 -14
- package/docs/goals/query-parser.md +98 -4
- package/docs/goals/query-result.md +177 -10
- package/docs/goals/skills.md +72 -0
- package/docs/goals/worker-daemon.md +54 -7
- package/docs/modules/rust/fs-database/functions/integrity.md +11 -0
- package/docs/modules/rust/fs-database/functions/section.md +23 -6
- package/docs/modules/rust/fs-database/module.md +1 -0
- package/docs/modules/rust/fs-database/types/section.md +27 -1
- package/docs/modules/rust/overview.md +2 -1
- package/docs/modules/rust/query-parser/functions/module.md +2 -2
- package/docs/modules/rust/query-parser/functions/pipeline.md +44 -6
- package/docs/modules/rust/query-parser/module.md +5 -4
- package/docs/modules/rust/query-parser/types/keyphrase.md +11 -0
- package/docs/modules/rust/query-parser/types/module.md +3 -3
- package/docs/modules/typescript/shared/cli/functions.md +68 -3
- package/package.json +1 -1
- package/src/shared/cli/functions/dispatch-command.function.ts +53 -1
- package/src/shared/cli/functions/format-output.function.ts +36 -12
- package/src/shared/cli/functions/generate-help.function.ts +56 -1
- package/src/shared/cli/functions/install-default-skills.function.ts +296 -0
- package/src/shared/cli/functions/parse-args.function.ts +13 -0
- package/src/shared/cli/functions/run-check.function.ts +87 -6
- package/src/shared/cli/functions/run-export.function.ts +40 -0
- package/src/shared/cli/functions/run-gc.function.ts +6 -0
- package/src/shared/cli/functions/run-init.function.ts +2 -0
- package/src/shared/cli/functions/run-learn.function.ts +68 -16
- package/src/shared/cli/functions/run-recall.function.ts +66 -0
- package/src/shared/cli/functions/run-search.function.ts +58 -15
- package/src/shared/cli/index.ts +4 -0
- package/src/shared/cli/typings/cli-options.interface.ts +13 -0
- package/src/shared/cli/typings/learn-result.model.ts +16 -0
- package/src/shared/cli/typings/search-display.model.ts +18 -0
- package/src/shared/mcp/classes/mcp-server.class.ts +30 -1
- package/src/shared/mcp/functions/create-tool-definitions.function.ts +46 -5
- package/src/shared/mcp/functions/handle-tool-call.function.ts +70 -2
- package/src/shared/mcp/typings/mcp-client.interface.ts +45 -0
- package/src/shared/version/constants/version.constant.ts +1 -1
- package/src/shared/wasm-codec/functions/decode-add-section-result.function.ts +112 -0
- package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +86 -0
- package/src/shared/wasm-codec/functions/decode-section-records.function.ts +87 -0
- package/src/shared/wasm-codec/functions/encode-add-section-params.function.ts +68 -0
- package/src/shared/wasm-codec/index.ts +5 -0
- package/src/shared/worker-client/classes/worker-client.class.ts +19 -0
- package/src/shared/worker-client/index.ts +4 -1
- package/src/shared/worker-client/typings/add-section-result.model.ts +30 -15
- package/src/shared/worker-client/typings/export-result.model.ts +14 -0
- package/src/shared/worker-client/typings/gc-result.model.ts +6 -0
- package/src/shared/worker-client/typings/search-result.model.ts +12 -0
- package/src/shared/worker-client/typings/section.model.ts +16 -0
- package/src/shared/worker-daemon/classes/worker-daemon.class.ts +315 -46
- package/src/shared/worker-daemon/functions/create-host-imports.function.ts +19 -4
- package/src/shared/worker-daemon/functions/create-ipc-server.function.ts +5 -1
- package/src/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
- package/src/wasm/fs-database/kbdb_fs_database.js +28 -1
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
- package/src/wasm/kb-worker/kbdb_worker.d.ts +48 -1
- package/src/wasm/kb-worker/kbdb_worker.js +60 -1
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
- package/src/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
package/dist/kbdb-worker.cjs
CHANGED
|
@@ -171,8 +171,9 @@ function createHostImports(contextPath, memoryRef = nullMemory, fs = nodeFsAdapt
|
|
|
171
171
|
}
|
|
172
172
|
function readPathArg(ptr, len) {
|
|
173
173
|
const bytes = new Uint8Array(getBuffer(), ptr, len);
|
|
174
|
-
const
|
|
175
|
-
|
|
174
|
+
const decoded = new TextDecoder().decode(bytes);
|
|
175
|
+
if (decoded.startsWith("/")) return decoded;
|
|
176
|
+
return (0, import_node_path4.join)(contextPath, decoded);
|
|
176
177
|
}
|
|
177
178
|
return {
|
|
178
179
|
/**
|
|
@@ -192,14 +193,15 @@ function createHostImports(contextPath, memoryRef = nullMemory, fs = nodeFsAdapt
|
|
|
192
193
|
const filePath = readPathArg(pathPtr, pathLen);
|
|
193
194
|
const data = fs.readFileSync(filePath);
|
|
194
195
|
const dataLen = data.byteLength;
|
|
195
|
-
const dv = new DataView(getBuffer());
|
|
196
196
|
if (dataLen === 0) {
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
const dv2 = new DataView(getBuffer());
|
|
198
|
+
dv2.setUint32(outPtr, 0, true);
|
|
199
|
+
dv2.setUint32(outLen, 0, true);
|
|
199
200
|
return 0;
|
|
200
201
|
}
|
|
201
202
|
const dataPtr = allocator.alloc(dataLen);
|
|
202
203
|
if (dataPtr === 0) return -1;
|
|
204
|
+
const dv = new DataView(getBuffer());
|
|
203
205
|
const memView = new Uint8Array(getBuffer());
|
|
204
206
|
memView.set(
|
|
205
207
|
new Uint8Array(
|
|
@@ -277,14 +279,15 @@ function createHostImports(contextPath, memoryRef = nullMemory, fs = nodeFsAdapt
|
|
|
277
279
|
const entries = fs.readdirSync(dirPath);
|
|
278
280
|
const text = entries.join("\n");
|
|
279
281
|
const encoded = new TextEncoder().encode(text);
|
|
280
|
-
const dv = new DataView(getBuffer());
|
|
281
282
|
if (encoded.length === 0) {
|
|
282
|
-
|
|
283
|
-
|
|
283
|
+
const dv2 = new DataView(getBuffer());
|
|
284
|
+
dv2.setUint32(outPtr, 0, true);
|
|
285
|
+
dv2.setUint32(outLen, 0, true);
|
|
284
286
|
return 0;
|
|
285
287
|
}
|
|
286
288
|
const dataPtr = allocator.alloc(encoded.length);
|
|
287
289
|
if (dataPtr === 0) return -1;
|
|
290
|
+
const dv = new DataView(getBuffer());
|
|
288
291
|
new Uint8Array(getBuffer(), dataPtr, encoded.length).set(
|
|
289
292
|
encoded
|
|
290
293
|
);
|
|
@@ -388,13 +391,16 @@ var import_node_net = require("node:net");
|
|
|
388
391
|
function createIpcServer(socketPath, handler) {
|
|
389
392
|
const server = (0, import_node_net.createServer)((socket) => {
|
|
390
393
|
let buffer = "";
|
|
394
|
+
let processingQueue = Promise.resolve();
|
|
391
395
|
socket.on("data", (chunk) => {
|
|
392
396
|
buffer += chunk.toString();
|
|
393
397
|
const lines = buffer.split("\n");
|
|
394
398
|
buffer = lines.pop() ?? "";
|
|
395
399
|
for (const line of lines) {
|
|
396
400
|
if (!line.trim()) continue;
|
|
397
|
-
|
|
401
|
+
processingQueue = processingQueue.then(
|
|
402
|
+
() => handleLine(line, handler, socket)
|
|
403
|
+
);
|
|
398
404
|
}
|
|
399
405
|
});
|
|
400
406
|
socket.on("error", (err) => {
|
|
@@ -492,13 +498,27 @@ function encodeSectionType(kind, mimeType) {
|
|
|
492
498
|
|
|
493
499
|
// src/shared/wasm-codec/functions/encode-add-section-params.function.ts
|
|
494
500
|
function encodeAddSectionParams(params) {
|
|
501
|
+
const tags = params.tags ?? [];
|
|
502
|
+
const tagParts = [];
|
|
503
|
+
for (const tag of tags) {
|
|
504
|
+
tagParts.push(encodeString(tag));
|
|
505
|
+
}
|
|
506
|
+
const tagsCountBytes = encodeU32(tags.length);
|
|
507
|
+
const replaceByte = new Uint8Array([
|
|
508
|
+
params.replace === true ? 1 : 0
|
|
509
|
+
]);
|
|
495
510
|
const parts = [
|
|
496
511
|
encodeSectionType(params.sectionType, params.mimeType),
|
|
497
512
|
encodeString(params.content),
|
|
498
513
|
encodeOptionString(params.title),
|
|
499
514
|
encodeOptionString(params.description),
|
|
500
515
|
encodeOptionString(params.docid),
|
|
501
|
-
encodeOptionString(params.typeName)
|
|
516
|
+
encodeOptionString(params.typeName),
|
|
517
|
+
encodeOptionString(params.sourcePath),
|
|
518
|
+
encodeOptionString(params.sourceMtime),
|
|
519
|
+
tagsCountBytes,
|
|
520
|
+
...tagParts,
|
|
521
|
+
replaceByte
|
|
502
522
|
];
|
|
503
523
|
const totalLen = parts.reduce((sum, p) => sum + p.length, 0);
|
|
504
524
|
const result = new Uint8Array(totalLen);
|
|
@@ -655,6 +675,10 @@ function decodePagedSearchResult(bytes) {
|
|
|
655
675
|
offset += limit.bytesRead;
|
|
656
676
|
const hasMore = bytes[offset] === 1;
|
|
657
677
|
offset += 1;
|
|
678
|
+
const relaxed = offset < bytes.length && bytes[offset] === 1;
|
|
679
|
+
if (offset < bytes.length) {
|
|
680
|
+
offset += 1;
|
|
681
|
+
}
|
|
658
682
|
const itemCount = decodeU32(bytes, offset);
|
|
659
683
|
offset += itemCount.bytesRead;
|
|
660
684
|
const items = [];
|
|
@@ -675,6 +699,8 @@ function decodePagedSearchResult(bytes) {
|
|
|
675
699
|
}
|
|
676
700
|
const score = decodeF32(bytes, offset);
|
|
677
701
|
offset += score.bytesRead;
|
|
702
|
+
const confidence = decodeF32(bytes, offset);
|
|
703
|
+
offset += confidence.bytesRead;
|
|
678
704
|
const snippet = decodeString(bytes, offset);
|
|
679
705
|
offset += snippet.bytesRead;
|
|
680
706
|
const termCount = decodeU32(bytes, offset);
|
|
@@ -685,14 +711,27 @@ function decodePagedSearchResult(bytes) {
|
|
|
685
711
|
offset += term.bytesRead;
|
|
686
712
|
matched_terms.push(term.value);
|
|
687
713
|
}
|
|
714
|
+
const fieldCount = decodeU32(bytes, offset);
|
|
715
|
+
offset += fieldCount.bytesRead;
|
|
716
|
+
const matched_fields = [];
|
|
717
|
+
for (let j = 0; j < fieldCount.value; j++) {
|
|
718
|
+
const field = decodeString(bytes, offset);
|
|
719
|
+
offset += field.bytesRead;
|
|
720
|
+
matched_fields.push(field.value);
|
|
721
|
+
}
|
|
722
|
+
const createdAt = decodeString(bytes, offset);
|
|
723
|
+
offset += createdAt.bytesRead;
|
|
688
724
|
items.push({
|
|
689
725
|
kbid: kbid.value,
|
|
690
726
|
heading: heading.value,
|
|
691
727
|
type: sectionType.value,
|
|
692
728
|
docids,
|
|
693
729
|
score: score.value,
|
|
730
|
+
confidence: confidence.value,
|
|
694
731
|
snippet: snippet.value,
|
|
695
|
-
matched_terms
|
|
732
|
+
matched_terms,
|
|
733
|
+
matched_fields,
|
|
734
|
+
created_at: createdAt.value
|
|
696
735
|
});
|
|
697
736
|
}
|
|
698
737
|
return {
|
|
@@ -700,7 +739,8 @@ function decodePagedSearchResult(bytes) {
|
|
|
700
739
|
total: total.value,
|
|
701
740
|
offset: resultOffset.value,
|
|
702
741
|
limit: limit.value,
|
|
703
|
-
has_more: hasMore
|
|
742
|
+
has_more: hasMore,
|
|
743
|
+
relaxed
|
|
704
744
|
};
|
|
705
745
|
}
|
|
706
746
|
|
|
@@ -760,6 +800,47 @@ function decodeSectionRecords(bytes) {
|
|
|
760
800
|
offset += codeTokenCount.bytesRead;
|
|
761
801
|
const typeName = decodeString(bytes, offset);
|
|
762
802
|
offset += typeName.bytesRead;
|
|
803
|
+
let sourceKey = null;
|
|
804
|
+
if (offset < bytes.length) {
|
|
805
|
+
const v = decodeOptionString2(bytes, offset);
|
|
806
|
+
offset += v.bytesRead;
|
|
807
|
+
sourceKey = v.value;
|
|
808
|
+
}
|
|
809
|
+
let sourcePath = null;
|
|
810
|
+
if (offset < bytes.length) {
|
|
811
|
+
const v = decodeOptionString2(bytes, offset);
|
|
812
|
+
offset += v.bytesRead;
|
|
813
|
+
sourcePath = v.value;
|
|
814
|
+
}
|
|
815
|
+
let sourceMtime = null;
|
|
816
|
+
if (offset < bytes.length) {
|
|
817
|
+
const v = decodeOptionString2(bytes, offset);
|
|
818
|
+
offset += v.bytesRead;
|
|
819
|
+
sourceMtime = v.value;
|
|
820
|
+
}
|
|
821
|
+
let updatedAt = null;
|
|
822
|
+
if (offset < bytes.length) {
|
|
823
|
+
const v = decodeOptionString2(bytes, offset);
|
|
824
|
+
offset += v.bytesRead;
|
|
825
|
+
updatedAt = v.value;
|
|
826
|
+
}
|
|
827
|
+
let tags = [];
|
|
828
|
+
if (offset < bytes.length) {
|
|
829
|
+
const tagCount = decodeU32(bytes, offset);
|
|
830
|
+
offset += tagCount.bytesRead;
|
|
831
|
+
tags = [];
|
|
832
|
+
for (let j = 0; j < tagCount.value; j++) {
|
|
833
|
+
const tag = decodeString(bytes, offset);
|
|
834
|
+
offset += tag.bytesRead;
|
|
835
|
+
tags.push(tag.value);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
let supersedes = null;
|
|
839
|
+
if (offset < bytes.length) {
|
|
840
|
+
const v = decodeOptionString2(bytes, offset);
|
|
841
|
+
offset += v.bytesRead;
|
|
842
|
+
supersedes = v.value;
|
|
843
|
+
}
|
|
763
844
|
const record = {
|
|
764
845
|
kbid: kbid.value,
|
|
765
846
|
docid: docid.value,
|
|
@@ -774,7 +855,13 @@ function decodeSectionRecords(bytes) {
|
|
|
774
855
|
headingTokenCount: headingTokenCount.value,
|
|
775
856
|
bodyTokenCount: bodyTokenCount.value,
|
|
776
857
|
codeTokenCount: codeTokenCount.value,
|
|
777
|
-
typeName: typeName.value
|
|
858
|
+
typeName: typeName.value,
|
|
859
|
+
sourceKey,
|
|
860
|
+
sourcePath,
|
|
861
|
+
sourceMtime,
|
|
862
|
+
updatedAt,
|
|
863
|
+
tags,
|
|
864
|
+
supersedes
|
|
778
865
|
};
|
|
779
866
|
if (st.mimeType !== void 0) {
|
|
780
867
|
record.mimeType = st.mimeType;
|
|
@@ -1094,6 +1181,54 @@ function decodeOneResult(bytes, startOffset) {
|
|
|
1094
1181
|
};
|
|
1095
1182
|
}
|
|
1096
1183
|
|
|
1184
|
+
// src/shared/wasm-codec/functions/decode-add-section-result.function.ts
|
|
1185
|
+
function decodeAddSectionResult(bytes) {
|
|
1186
|
+
let offset = 0;
|
|
1187
|
+
const kbid = decodeString(bytes, offset);
|
|
1188
|
+
offset += kbid.bytesRead;
|
|
1189
|
+
const indexedTermsCount = decodeU32(bytes, offset);
|
|
1190
|
+
offset += indexedTermsCount.bytesRead;
|
|
1191
|
+
const elapsedMsBig = decodeU64(bytes, offset);
|
|
1192
|
+
offset += elapsedMsBig.bytesRead;
|
|
1193
|
+
const supersededTag = bytes[offset];
|
|
1194
|
+
offset += 1;
|
|
1195
|
+
let superseded = null;
|
|
1196
|
+
if (supersededTag === 1) {
|
|
1197
|
+
const supersededStr = decodeString(bytes, offset);
|
|
1198
|
+
offset += supersededStr.bytesRead;
|
|
1199
|
+
superseded = supersededStr.value;
|
|
1200
|
+
}
|
|
1201
|
+
const warningCount = decodeU32(bytes, offset);
|
|
1202
|
+
offset += warningCount.bytesRead;
|
|
1203
|
+
const warnings = [];
|
|
1204
|
+
for (let i = 0; i < warningCount.value; i++) {
|
|
1205
|
+
const warning = decodeString(bytes, offset);
|
|
1206
|
+
offset += warning.bytesRead;
|
|
1207
|
+
warnings.push(warning.value);
|
|
1208
|
+
}
|
|
1209
|
+
const dupCount = decodeU32(bytes, offset);
|
|
1210
|
+
offset += dupCount.bytesRead;
|
|
1211
|
+
const nearDuplicates = [];
|
|
1212
|
+
for (let i = 0; i < dupCount.value; i++) {
|
|
1213
|
+
const dupKbid = decodeString(bytes, offset);
|
|
1214
|
+
offset += dupKbid.bytesRead;
|
|
1215
|
+
const similarity = decodeF32(bytes, offset);
|
|
1216
|
+
offset += similarity.bytesRead;
|
|
1217
|
+
nearDuplicates.push({
|
|
1218
|
+
kbid: dupKbid.value,
|
|
1219
|
+
similarity: similarity.value
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
return {
|
|
1223
|
+
kbid: kbid.value,
|
|
1224
|
+
indexedTermsCount: indexedTermsCount.value,
|
|
1225
|
+
elapsedMs: Number(elapsedMsBig.value),
|
|
1226
|
+
superseded,
|
|
1227
|
+
warnings,
|
|
1228
|
+
nearDuplicates
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1097
1232
|
// src/shared/worker-daemon/classes/worker-daemon.class.ts
|
|
1098
1233
|
var WASM_METHOD_MAP = {
|
|
1099
1234
|
search: "worker_search",
|
|
@@ -1133,7 +1268,9 @@ var WASM_METHOD_MAP = {
|
|
|
1133
1268
|
list_by_type: "worker_list_by_type",
|
|
1134
1269
|
listDocumentsByType: "worker_list_docs_by_type",
|
|
1135
1270
|
list_docs_by_type: "worker_list_docs_by_type",
|
|
1136
|
-
recall: "worker_recall"
|
|
1271
|
+
recall: "worker_recall",
|
|
1272
|
+
export: "worker_export",
|
|
1273
|
+
exportDb: "worker_export"
|
|
1137
1274
|
};
|
|
1138
1275
|
var CONFIG_PREFIX = "config_";
|
|
1139
1276
|
var CONFIG_KEY_MAP = {
|
|
@@ -1168,6 +1305,14 @@ var WorkerDaemon = class {
|
|
|
1168
1305
|
_idleTimer = null;
|
|
1169
1306
|
_server = null;
|
|
1170
1307
|
_wasmDir;
|
|
1308
|
+
/** Queue of waiters for the single-writer lock. */
|
|
1309
|
+
_writeQueue = [];
|
|
1310
|
+
/** Whether the write lock is currently held. */
|
|
1311
|
+
_writeLocked = false;
|
|
1312
|
+
/** Whether the startup integrity check passed. */
|
|
1313
|
+
_integrityOk = true;
|
|
1314
|
+
/** Integrity errors found at startup, if any. */
|
|
1315
|
+
_integrityErrors = [];
|
|
1171
1316
|
/**
|
|
1172
1317
|
* @param ctx - context ID (16-hex characters), the first 16 of
|
|
1173
1318
|
* the SHA-256 hash of `targetDir`
|
|
@@ -1229,6 +1374,7 @@ var WorkerDaemon = class {
|
|
|
1229
1374
|
);
|
|
1230
1375
|
this._wasmModules = wasmModules;
|
|
1231
1376
|
this.initWorkerContext();
|
|
1377
|
+
this.runStartupIntegrityCheck();
|
|
1232
1378
|
const pidPath = (0, import_node_path6.join)((0, import_node_os2.tmpdir)(), `kbdb-${this.ctx}.pid`);
|
|
1233
1379
|
(0, import_node_fs4.writeFileSync)(pidPath, process.pid.toString());
|
|
1234
1380
|
const ipcPath = getIpcPath(this.ctx);
|
|
@@ -1417,7 +1563,11 @@ var WorkerDaemon = class {
|
|
|
1417
1563
|
*/
|
|
1418
1564
|
dispatch(method, params) {
|
|
1419
1565
|
if (method === "status") {
|
|
1420
|
-
return
|
|
1566
|
+
return {
|
|
1567
|
+
status: this._connectionCount > 0 ? "busy" : "idle",
|
|
1568
|
+
integrityOk: this._integrityOk,
|
|
1569
|
+
integrityErrors: this._integrityErrors
|
|
1570
|
+
};
|
|
1421
1571
|
}
|
|
1422
1572
|
if (method === "idle_at") {
|
|
1423
1573
|
return this._idleAt;
|
|
@@ -1434,67 +1584,31 @@ var WorkerDaemon = class {
|
|
|
1434
1584
|
}
|
|
1435
1585
|
throw new IpcError(-32601, `Method not found: ${method}`);
|
|
1436
1586
|
}
|
|
1437
|
-
dispatchWasm(method, wasmExport, params) {
|
|
1587
|
+
async dispatchWasm(method, wasmExport, params) {
|
|
1438
1588
|
if (method === "search" || method === "query") {
|
|
1439
1589
|
return this.handleSearch(params);
|
|
1440
1590
|
}
|
|
1441
|
-
if (method === "addSection" || method === "add_section") {
|
|
1442
|
-
return this.handleAddSection(params);
|
|
1443
|
-
}
|
|
1444
|
-
if (method === "updateSection" || method === "update_section") {
|
|
1445
|
-
return this.handleUpdateSection(params);
|
|
1446
|
-
}
|
|
1447
|
-
if (method === "removeSection" || method === "remove_section") {
|
|
1448
|
-
return this.handleRemoveSection(params);
|
|
1449
|
-
}
|
|
1450
1591
|
if (method === "readSections" || method === "read_sections") {
|
|
1451
1592
|
return this.handleReadSections(params);
|
|
1452
1593
|
}
|
|
1453
1594
|
if (method === "retrieveDocument" || method === "retrieve_doc") {
|
|
1454
1595
|
return this.handleRetrieveDoc(params);
|
|
1455
1596
|
}
|
|
1456
|
-
if (method === "groupSections" || method === "group_sections") {
|
|
1457
|
-
return this.handleGroupSections(params);
|
|
1458
|
-
}
|
|
1459
|
-
if (method === "removeGrouping" || method === "remove_grouping") {
|
|
1460
|
-
return this.handleRemoveGrouping(params);
|
|
1461
|
-
}
|
|
1462
1597
|
if (method === "content" || method === "compose") {
|
|
1463
1598
|
return this.handleCompose(params);
|
|
1464
1599
|
}
|
|
1465
1600
|
if (method === "integrityCheck" || method === "integrity_check") {
|
|
1466
1601
|
return this.handleIntegrityCheck();
|
|
1467
1602
|
}
|
|
1468
|
-
if (method === "gc") {
|
|
1469
|
-
return this.handleGc();
|
|
1470
|
-
}
|
|
1471
|
-
if (method === "rebuildIndexes" || method === "rebuild_indexes") {
|
|
1472
|
-
return this.handleRebuild();
|
|
1473
|
-
}
|
|
1474
1603
|
if (method === "dbStatus" || method === "db_status") {
|
|
1475
1604
|
return this.handleDbStatus();
|
|
1476
1605
|
}
|
|
1477
|
-
if (method === "initDirectory" || method === "init_directory") {
|
|
1478
|
-
return this.handleInitDirectory(params);
|
|
1479
|
-
}
|
|
1480
|
-
if (method === "invalidate") {
|
|
1481
|
-
return this.handleInvalidate(params);
|
|
1482
|
-
}
|
|
1483
|
-
if (method === "invalidateAll" || method === "invalidate_all") {
|
|
1484
|
-
return this.handleInvalidateAll();
|
|
1485
|
-
}
|
|
1486
1606
|
if (method === "get_result_status") {
|
|
1487
1607
|
return this.handleResultStatus(params);
|
|
1488
1608
|
}
|
|
1489
1609
|
if (method === "get_result") {
|
|
1490
1610
|
return this.handleGetResult(params);
|
|
1491
1611
|
}
|
|
1492
|
-
if (method === "migrateDatabase") {
|
|
1493
|
-
throw new IpcError(
|
|
1494
|
-
-32601,
|
|
1495
|
-
"migrateDatabase is not yet supported"
|
|
1496
|
-
);
|
|
1497
|
-
}
|
|
1498
1612
|
if (method === "listByType" || method === "list_by_type") {
|
|
1499
1613
|
return this.handleListByType(params);
|
|
1500
1614
|
}
|
|
@@ -1504,7 +1618,51 @@ var WorkerDaemon = class {
|
|
|
1504
1618
|
if (method === "recall") {
|
|
1505
1619
|
return this.handleRecall(params);
|
|
1506
1620
|
}
|
|
1507
|
-
|
|
1621
|
+
if (method === "export" || method === "exportDb") {
|
|
1622
|
+
return this.handleExport(params);
|
|
1623
|
+
}
|
|
1624
|
+
await this.acquireWriteLock();
|
|
1625
|
+
try {
|
|
1626
|
+
if (method === "invalidate") {
|
|
1627
|
+
return this.handleInvalidate(params);
|
|
1628
|
+
}
|
|
1629
|
+
if (method === "invalidateAll" || method === "invalidate_all") {
|
|
1630
|
+
return this.handleInvalidateAll();
|
|
1631
|
+
}
|
|
1632
|
+
if (method === "addSection" || method === "add_section") {
|
|
1633
|
+
return this.handleAddSection(params);
|
|
1634
|
+
}
|
|
1635
|
+
if (method === "updateSection" || method === "update_section") {
|
|
1636
|
+
return this.handleUpdateSection(params);
|
|
1637
|
+
}
|
|
1638
|
+
if (method === "removeSection" || method === "remove_section") {
|
|
1639
|
+
return this.handleRemoveSection(params);
|
|
1640
|
+
}
|
|
1641
|
+
if (method === "groupSections" || method === "group_sections") {
|
|
1642
|
+
return this.handleGroupSections(params);
|
|
1643
|
+
}
|
|
1644
|
+
if (method === "removeGrouping" || method === "remove_grouping") {
|
|
1645
|
+
return this.handleRemoveGrouping(params);
|
|
1646
|
+
}
|
|
1647
|
+
if (method === "gc") {
|
|
1648
|
+
return this.handleGc();
|
|
1649
|
+
}
|
|
1650
|
+
if (method === "rebuildIndexes" || method === "rebuild_indexes") {
|
|
1651
|
+
return this.handleRebuild();
|
|
1652
|
+
}
|
|
1653
|
+
if (method === "initDirectory" || method === "init_directory") {
|
|
1654
|
+
return this.handleInitDirectory(params);
|
|
1655
|
+
}
|
|
1656
|
+
if (method === "migrateDatabase") {
|
|
1657
|
+
throw new IpcError(
|
|
1658
|
+
-32601,
|
|
1659
|
+
"migrateDatabase is not yet supported"
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
return this.invokeWasmRaw(wasmExport, new Uint8Array(0));
|
|
1663
|
+
} finally {
|
|
1664
|
+
this.releaseWriteLock();
|
|
1665
|
+
}
|
|
1508
1666
|
}
|
|
1509
1667
|
// -- Search/query (token-based async) --
|
|
1510
1668
|
handleSearch(params) {
|
|
@@ -1559,6 +1717,7 @@ var WorkerDaemon = class {
|
|
|
1559
1717
|
const sectionType = params["sectionType"] ?? "text";
|
|
1560
1718
|
const semanticType = sectionType === "text" || sectionType === "code" || sectionType === "image" ? sectionType : "text";
|
|
1561
1719
|
const typeName = sectionType !== semanticType ? sectionType : null;
|
|
1720
|
+
const tags = Array.isArray(params["tags"]) ? params["tags"] : [];
|
|
1562
1721
|
const input = {
|
|
1563
1722
|
sectionType: semanticType,
|
|
1564
1723
|
mimeType: params["mimeType"],
|
|
@@ -1566,12 +1725,24 @@ var WorkerDaemon = class {
|
|
|
1566
1725
|
title: params["title"] ?? null,
|
|
1567
1726
|
description: params["description"] ?? null,
|
|
1568
1727
|
docid: params["docid"] ?? null,
|
|
1569
|
-
typeName
|
|
1728
|
+
typeName,
|
|
1729
|
+
sourcePath: typeof params["sourcePath"] === "string" ? params["sourcePath"] : void 0,
|
|
1730
|
+
sourceMtime: typeof params["sourceMtime"] === "string" ? params["sourceMtime"] : void 0,
|
|
1731
|
+
tags,
|
|
1732
|
+
replace: params["replace"] === true
|
|
1570
1733
|
};
|
|
1571
1734
|
const encoded = encodeAddSectionParams(input);
|
|
1572
1735
|
const bytes = this.callWasmSync("worker_add_section", encoded);
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1736
|
+
const decoded = decodeAddSectionResult(bytes);
|
|
1737
|
+
const nearDups = decoded.nearDuplicates.length > 0 ? decoded.nearDuplicates : this.scanNearDuplicates(input.content, decoded.kbid);
|
|
1738
|
+
return {
|
|
1739
|
+
kbid: decoded.kbid,
|
|
1740
|
+
indexed_terms_count: decoded.indexedTermsCount,
|
|
1741
|
+
elapsed_ms: decoded.elapsedMs,
|
|
1742
|
+
superseded: decoded.superseded,
|
|
1743
|
+
warnings: decoded.warnings,
|
|
1744
|
+
near_duplicates: nearDups
|
|
1745
|
+
};
|
|
1575
1746
|
}
|
|
1576
1747
|
handleUpdateSection(params) {
|
|
1577
1748
|
const oldKbid = params["kbid"] ?? "";
|
|
@@ -1645,7 +1816,18 @@ var WorkerDaemon = class {
|
|
|
1645
1816
|
handleInvalidate(params) {
|
|
1646
1817
|
const kbid = params["kbid"] ?? "";
|
|
1647
1818
|
const encoded = new TextEncoder().encode(kbid);
|
|
1648
|
-
this.
|
|
1819
|
+
const fn = this.getExport("worker_invalidate");
|
|
1820
|
+
const ptr = this.wasmAlloc(encoded.length);
|
|
1821
|
+
this.wasmWrite(ptr, encoded);
|
|
1822
|
+
const rc = fn(ptr, encoded.length);
|
|
1823
|
+
this.wasmFree(ptr, encoded.length);
|
|
1824
|
+
if (rc === -1) {
|
|
1825
|
+
const errBytes = this.readReturnSlot();
|
|
1826
|
+
throw new IpcError(
|
|
1827
|
+
-32e3,
|
|
1828
|
+
new TextDecoder().decode(errBytes) || "invalidate failed"
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1649
1831
|
return { ok: true };
|
|
1650
1832
|
}
|
|
1651
1833
|
handleListByType(params) {
|
|
@@ -1689,20 +1871,173 @@ var WorkerDaemon = class {
|
|
|
1689
1871
|
return decodeKeyValueText(text);
|
|
1690
1872
|
}
|
|
1691
1873
|
handleGc() {
|
|
1874
|
+
const start = Date.now();
|
|
1692
1875
|
this.callWasmZeroArg("worker_garbage_collect");
|
|
1693
1876
|
const bytes = this.readReturnSlot();
|
|
1694
1877
|
const text = new TextDecoder().decode(bytes);
|
|
1695
|
-
|
|
1878
|
+
const kv = decodeKeyValueText(text);
|
|
1879
|
+
const removedCount = Number(kv["removed"] ?? 0);
|
|
1880
|
+
const staleSources = Number(kv["stale_sources"] ?? 0);
|
|
1881
|
+
return {
|
|
1882
|
+
removed_sections: removedCount,
|
|
1883
|
+
removed_bytes: 0,
|
|
1884
|
+
elapsed_ms: Date.now() - start,
|
|
1885
|
+
stale_sources: staleSources
|
|
1886
|
+
};
|
|
1696
1887
|
}
|
|
1697
1888
|
handleRebuild() {
|
|
1698
|
-
this.
|
|
1699
|
-
|
|
1889
|
+
const fn = this.getExport("worker_rebuild_indexes");
|
|
1890
|
+
const rc = fn();
|
|
1891
|
+
if (rc === -1) {
|
|
1892
|
+
const errBytes = this.readReturnSlot();
|
|
1893
|
+
const errMsg = new TextDecoder().decode(errBytes);
|
|
1894
|
+
throw new IpcError(-32e3, errMsg || "rebuild failed");
|
|
1895
|
+
}
|
|
1896
|
+
const resultBytes = this.readReturnSlot();
|
|
1897
|
+
const reindexed = resultBytes.length >= 4 ? new DataView(
|
|
1898
|
+
resultBytes.buffer,
|
|
1899
|
+
resultBytes.byteOffset,
|
|
1900
|
+
resultBytes.byteLength
|
|
1901
|
+
).getUint32(0, true) : 0;
|
|
1902
|
+
return { ok: true, sections_reindexed: reindexed };
|
|
1903
|
+
}
|
|
1904
|
+
// -- Export (read operation, no lock required) --
|
|
1905
|
+
handleExport(params) {
|
|
1906
|
+
const targetPath = params["targetPath"] ?? params["path"] ?? "./kbdb-export/";
|
|
1907
|
+
const encoded = new TextEncoder().encode(targetPath);
|
|
1908
|
+
const start = Date.now();
|
|
1909
|
+
const bytes = this.callWasmSync("worker_export", encoded);
|
|
1910
|
+
const elapsed = Date.now() - start;
|
|
1911
|
+
const text = new TextDecoder().decode(bytes);
|
|
1912
|
+
const kv = decodeKeyValueText(text);
|
|
1913
|
+
return {
|
|
1914
|
+
path: kv["path"] ?? targetPath,
|
|
1915
|
+
sections_exported: Number(kv["sections_exported"] ?? 0),
|
|
1916
|
+
documents_exported: Number(kv["documents_exported"] ?? 0),
|
|
1917
|
+
elapsed_ms: elapsed
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
// -- Near-duplicate detection --
|
|
1921
|
+
scanNearDuplicates(content, newKbid) {
|
|
1922
|
+
const newTerms = this.extractTermSet(content);
|
|
1923
|
+
if (newTerms.size === 0) return [];
|
|
1924
|
+
const sectionsDir = (0, import_node_path6.join)(this.targetDir, "sections");
|
|
1925
|
+
let shards;
|
|
1926
|
+
try {
|
|
1927
|
+
shards = (0, import_node_fs4.readdirSync)(sectionsDir);
|
|
1928
|
+
} catch {
|
|
1929
|
+
return [];
|
|
1930
|
+
}
|
|
1931
|
+
const results = [];
|
|
1932
|
+
for (const shard of shards) {
|
|
1933
|
+
let files;
|
|
1934
|
+
try {
|
|
1935
|
+
files = (0, import_node_fs4.readdirSync)((0, import_node_path6.join)(sectionsDir, shard));
|
|
1936
|
+
} catch {
|
|
1937
|
+
continue;
|
|
1938
|
+
}
|
|
1939
|
+
for (const file of files) {
|
|
1940
|
+
if (!file.endsWith(".section")) continue;
|
|
1941
|
+
const kbid = file.slice(0, -8);
|
|
1942
|
+
if (kbid === newKbid) continue;
|
|
1943
|
+
try {
|
|
1944
|
+
const data = (0, import_node_fs4.readFileSync)(
|
|
1945
|
+
(0, import_node_path6.join)(sectionsDir, shard, file),
|
|
1946
|
+
"utf-8"
|
|
1947
|
+
);
|
|
1948
|
+
const sep = data.indexOf("\n---\n");
|
|
1949
|
+
const body = sep >= 0 ? data.slice(sep + 5) : data;
|
|
1950
|
+
const existing = this.extractTermSet(body);
|
|
1951
|
+
let intersection = 0;
|
|
1952
|
+
for (const t of newTerms) {
|
|
1953
|
+
if (existing.has(t)) intersection++;
|
|
1954
|
+
}
|
|
1955
|
+
const union = newTerms.size + existing.size - intersection;
|
|
1956
|
+
const sim = union > 0 ? intersection / union : 0;
|
|
1957
|
+
if (sim >= 0.85) {
|
|
1958
|
+
results.push({
|
|
1959
|
+
kbid,
|
|
1960
|
+
similarity: Math.round(sim * 100) / 100
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
} catch {
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return results;
|
|
1969
|
+
}
|
|
1970
|
+
extractTermSet(text) {
|
|
1971
|
+
return new Set(
|
|
1972
|
+
text.toLowerCase().split(/\W+/).filter((w) => w.length > 2)
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
// -- Single-writer lock --
|
|
1976
|
+
/**
|
|
1977
|
+
* Acquires the write lock.
|
|
1978
|
+
*
|
|
1979
|
+
* If the lock is free it is granted immediately. If it is held the
|
|
1980
|
+
* caller is queued and its promise resolves once the lock is
|
|
1981
|
+
* released by the current holder.
|
|
1982
|
+
*/
|
|
1983
|
+
acquireWriteLock() {
|
|
1984
|
+
if (!this._writeLocked) {
|
|
1985
|
+
this._writeLocked = true;
|
|
1986
|
+
return Promise.resolve();
|
|
1987
|
+
}
|
|
1988
|
+
return new Promise((resolve2) => {
|
|
1989
|
+
this._writeQueue.push({ resolve: resolve2 });
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
/**
|
|
1993
|
+
* Releases the write lock.
|
|
1994
|
+
*
|
|
1995
|
+
* If waiters are queued the next one is granted the lock
|
|
1996
|
+
* immediately. Otherwise the lock is marked free.
|
|
1997
|
+
*/
|
|
1998
|
+
releaseWriteLock() {
|
|
1999
|
+
const next = this._writeQueue.shift();
|
|
2000
|
+
if (next !== void 0) {
|
|
2001
|
+
next.resolve();
|
|
2002
|
+
} else {
|
|
2003
|
+
this._writeLocked = false;
|
|
2004
|
+
}
|
|
1700
2005
|
}
|
|
1701
2006
|
// -- WASM lifecycle --
|
|
1702
2007
|
initWorkerContext() {
|
|
1703
2008
|
const pathBytes = new TextEncoder().encode(".");
|
|
1704
2009
|
this.callWasmSync("worker_init", pathBytes);
|
|
1705
2010
|
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Runs a database integrity check immediately after context
|
|
2013
|
+
* initialisation.
|
|
2014
|
+
*
|
|
2015
|
+
* Stores the result in `_integrityOk` and `_integrityErrors`.
|
|
2016
|
+
* Logs a warning to stderr when issues are found so operators
|
|
2017
|
+
* are alerted without aborting the daemon.
|
|
2018
|
+
*/
|
|
2019
|
+
runStartupIntegrityCheck() {
|
|
2020
|
+
try {
|
|
2021
|
+
this.callWasmZeroArg("worker_check_integrity");
|
|
2022
|
+
const bytes = this.readReturnSlot();
|
|
2023
|
+
const text = new TextDecoder().decode(bytes);
|
|
2024
|
+
const kv = decodeKeyValueText(text);
|
|
2025
|
+
this._integrityOk = kv["ok"] === "true";
|
|
2026
|
+
if (!this._integrityOk) {
|
|
2027
|
+
this._integrityErrors = Object.entries(kv).filter(([k]) => k !== "ok").map(([k, v]) => `${k}=${v}`);
|
|
2028
|
+
console.error(
|
|
2029
|
+
"[kbdb-worker] startup integrity check failed:",
|
|
2030
|
+
this._integrityErrors.join(", ")
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
} catch {
|
|
2034
|
+
this._integrityOk = false;
|
|
2035
|
+
this._integrityErrors = ["integrity_check_failed"];
|
|
2036
|
+
console.error(
|
|
2037
|
+
"[kbdb-worker] startup integrity check threw an error"
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1706
2041
|
// -- WASM bridge primitives --
|
|
1707
2042
|
getExport(name) {
|
|
1708
2043
|
if (this._wasmModules === null) {
|