@ontos-ai/knowhere-sdk 2.6.1-beta.20261027152313 → 2.7.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/dist/{chunk-DIONXJWK.mjs → chunk-OCR7IDYL.mjs} +128 -101
- package/dist/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +352 -124
- package/dist/index.mjs +188 -15
- package/dist/{sequential-parsed-document-archive-W7HPYIKA.mjs → sequential-parsed-document-archive-AIRFKO52.mjs} +27 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -258,9 +258,7 @@ var init_parsed_document_archive_error = __esm({
|
|
|
258
258
|
code = "ARCHIVE_SPOOL_FAILED";
|
|
259
259
|
cause;
|
|
260
260
|
constructor(cause) {
|
|
261
|
-
super(
|
|
262
|
-
"Parsed result ZIP temporary storage is unavailable. Range-enabled object storage avoids temporary disk usage."
|
|
263
|
-
);
|
|
261
|
+
super("Parsed result ZIP temporary storage is unavailable.");
|
|
264
262
|
this.name = "ParsedDocumentArchiveError";
|
|
265
263
|
this.cause = cause;
|
|
266
264
|
}
|
|
@@ -856,17 +854,15 @@ function readObjectVersion(object) {
|
|
|
856
854
|
return { entityTag: object.entityTag, contentLength: object.contentLength };
|
|
857
855
|
}
|
|
858
856
|
function validateCachedResponse(response, marker, signal) {
|
|
859
|
-
if (response.
|
|
857
|
+
if (response.headers.get("etag") !== marker.storageEntityTag)
|
|
860
858
|
rejectResponse(
|
|
861
859
|
response,
|
|
862
|
-
new ArchiveCacheTransportError(
|
|
863
|
-
`Parsed result archive cache requires a ranged response (HTTP ${response.status}).`
|
|
864
|
-
)
|
|
860
|
+
new ArchiveCacheTransportError("Parsed result archive cache response version changed.")
|
|
865
861
|
);
|
|
866
|
-
if (response.
|
|
862
|
+
if (response.status !== 206 || !response.body)
|
|
867
863
|
rejectResponse(
|
|
868
864
|
response,
|
|
869
|
-
new ArchiveCacheTransportError("Parsed result archive cache
|
|
865
|
+
new ArchiveCacheTransportError("Parsed result archive cache requires a ranged response.")
|
|
870
866
|
);
|
|
871
867
|
const range = /^bytes (\d+)-(\d+)\/(\d+)$/.exec(
|
|
872
868
|
response.headers.get("content-range") ?? ""
|
|
@@ -949,7 +945,10 @@ __export(sequential_parsed_document_archive_exports, {
|
|
|
949
945
|
openSequentialArchive: () => openSequentialArchive
|
|
950
946
|
});
|
|
951
947
|
async function openSequentialArchive(params, identity, validateResponse) {
|
|
952
|
-
let response = await fetch(params.resultUrl, {
|
|
948
|
+
let response = await fetch(params.resultUrl, {
|
|
949
|
+
signal: params.signal,
|
|
950
|
+
headers: validateResponse ? { Range: "bytes=0-" } : void 0
|
|
951
|
+
});
|
|
953
952
|
if (!response.ok || !response.body)
|
|
954
953
|
throw new Error(`Parsed result download failed (HTTP ${response.status}).`);
|
|
955
954
|
if (validateResponse) response = validateResponse(response);
|
|
@@ -978,9 +977,11 @@ async function openSequentialArchive(params, identity, validateResponse) {
|
|
|
978
977
|
const parser = (0, import_parse.default)({ forceStream: true });
|
|
979
978
|
const checksums = /* @__PURE__ */ new Map();
|
|
980
979
|
let archiveSize = 0;
|
|
980
|
+
const archiveHash = (0, import_crypto2.createHash)("sha256");
|
|
981
981
|
const spool = new import_stream.Transform({
|
|
982
982
|
transform(chunk, _encoding, callback) {
|
|
983
983
|
archiveSize += chunk.length;
|
|
984
|
+
archiveHash.update(chunk);
|
|
984
985
|
void file.writeFile(chunk).then(
|
|
985
986
|
() => {
|
|
986
987
|
callback(null, chunk);
|
|
@@ -997,6 +998,23 @@ async function openSequentialArchive(params, identity, validateResponse) {
|
|
|
997
998
|
identity,
|
|
998
999
|
downloadIdentity: identity,
|
|
999
1000
|
canSkipEntries: false,
|
|
1001
|
+
readArchive: () => {
|
|
1002
|
+
let position = 0;
|
|
1003
|
+
return new ReadableStream({
|
|
1004
|
+
async pull(controller) {
|
|
1005
|
+
params.signal?.throwIfAborted();
|
|
1006
|
+
if (position >= archiveSize) {
|
|
1007
|
+
controller.close();
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const body2 = new Uint8Array(Math.min(65536, archiveSize - position));
|
|
1011
|
+
const { bytesRead } = await file.read(body2, 0, body2.length, position);
|
|
1012
|
+
if (bytesRead !== body2.length) throw new Error("Truncated verified archive spool.");
|
|
1013
|
+
position += bytesRead;
|
|
1014
|
+
controller.enqueue(body2);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
},
|
|
1000
1018
|
entries: (async function* () {
|
|
1001
1019
|
for await (const value of parser) {
|
|
1002
1020
|
const entry = value;
|
|
@@ -1027,6 +1045,8 @@ async function openSequentialArchive(params, identity, validateResponse) {
|
|
|
1027
1045
|
};
|
|
1028
1046
|
}
|
|
1029
1047
|
await completed;
|
|
1048
|
+
if (params.snapshot && archiveHash.digest("hex") !== params.snapshot.checksum)
|
|
1049
|
+
throw new Error("Parsed snapshot archive checksum mismatch for extracted bytes.");
|
|
1030
1050
|
const reader = new import_zip.ZipReader(new DiskArchiveReader(file, archiveSize), {
|
|
1031
1051
|
useWebWorkers: false
|
|
1032
1052
|
});
|
|
@@ -1058,10 +1078,11 @@ async function openSequentialArchive(params, identity, validateResponse) {
|
|
|
1058
1078
|
}
|
|
1059
1079
|
};
|
|
1060
1080
|
}
|
|
1061
|
-
var import_stream, import_promises, import_promises2, import_os, import_path2, import_zlib, import_zip, import_parse, DiskArchiveReader;
|
|
1081
|
+
var import_crypto2, import_stream, import_promises, import_promises2, import_os, import_path2, import_zlib, import_zip, import_parse, DiskArchiveReader;
|
|
1062
1082
|
var init_sequential_parsed_document_archive = __esm({
|
|
1063
1083
|
"src/storage/sequential-parsed-document-archive.ts"() {
|
|
1064
1084
|
"use strict";
|
|
1085
|
+
import_crypto2 = require("crypto");
|
|
1065
1086
|
import_stream = require("stream");
|
|
1066
1087
|
import_promises = require("stream/promises");
|
|
1067
1088
|
import_promises2 = require("fs/promises");
|
|
@@ -1101,14 +1122,10 @@ async function syncRemoteResultArchive(params) {
|
|
|
1101
1122
|
const checkpointIntervalMs = params.limits?.checkpointIntervalMs ?? 2e3;
|
|
1102
1123
|
const controller = new AbortController();
|
|
1103
1124
|
const signal = params.signal ? AbortSignal.any([params.signal, controller.signal]) : controller.signal;
|
|
1104
|
-
const
|
|
1125
|
+
const transferParams = { ...params, signal };
|
|
1105
1126
|
const previous = await params.storage.readSyncProgress(params);
|
|
1106
|
-
const openedSource = await openArchive(
|
|
1127
|
+
const openedSource = await openArchive(transferParams, previous);
|
|
1107
1128
|
const source = !openedSource.canSkipEntries && previous?.archiveIdentity && previous.archiveDownloadIdentity === openedSource.downloadIdentity ? { ...openedSource, identity: previous.archiveIdentity } : openedSource;
|
|
1108
|
-
const transferParams = {
|
|
1109
|
-
...openingParams,
|
|
1110
|
-
signal: source.signal ?? signal
|
|
1111
|
-
};
|
|
1112
1129
|
const archiveDownloadIdentity = source.isCached ? source.downloadIdentity : previous?.archiveDownloadIdentity;
|
|
1113
1130
|
const concurrency = source.canSkipEntries ? params.limits?.maxConcurrentFiles ?? 1 : 1;
|
|
1114
1131
|
const nextEntry = previous?.status === "completed" ? 0 : previous?.nextArchiveEntry ?? 0;
|
|
@@ -1135,6 +1152,8 @@ async function syncRemoteResultArchive(params) {
|
|
|
1135
1152
|
documentId: params.documentId,
|
|
1136
1153
|
revisionKey: params.revisionKey,
|
|
1137
1154
|
pinnedRevisionKey: params.revisionKey,
|
|
1155
|
+
snapshotChecksum: params.snapshot?.checksum,
|
|
1156
|
+
upstreamJobResultId: params.snapshot?.jobResultId,
|
|
1138
1157
|
archiveIdentity: source.identity,
|
|
1139
1158
|
archiveDownloadIdentity,
|
|
1140
1159
|
archiveCacheBypassed,
|
|
@@ -1173,10 +1192,38 @@ async function syncRemoteResultArchive(params) {
|
|
|
1173
1192
|
signal.throwIfAborted();
|
|
1174
1193
|
}
|
|
1175
1194
|
try {
|
|
1176
|
-
if (previous?.
|
|
1195
|
+
if (previous?.snapshotChecksum && previous.snapshotChecksum !== params.snapshot?.checksum)
|
|
1196
|
+
throw new Error("Parsed snapshot checksum changed within one generation.");
|
|
1197
|
+
if (previous?.upstreamJobResultId && previous.upstreamJobResultId !== params.snapshot?.jobResultId)
|
|
1198
|
+
throw new Error("Parsed snapshot upstream revision changed within one generation.");
|
|
1199
|
+
if (previous?.archiveIdentity && previous.archiveIdentity !== source.identity) {
|
|
1177
1200
|
throw new Error("Parsed result archive identity changed; start a new revision.");
|
|
1178
|
-
|
|
1201
|
+
}
|
|
1202
|
+
if (previous?.pinnedRevisionKey && previous.pinnedRevisionKey !== params.revisionKey) {
|
|
1179
1203
|
throw new Error("Parsed result revision changed while resuming archive sync.");
|
|
1204
|
+
}
|
|
1205
|
+
if (params.snapshot && previous?.status === "completed") {
|
|
1206
|
+
const committed = await readParsedDocumentCommit(params);
|
|
1207
|
+
if (committed) {
|
|
1208
|
+
if (source.verifyArchiveChecksum) {
|
|
1209
|
+
await source.verifyArchiveChecksum(previous.archiveEntryHashes ?? []);
|
|
1210
|
+
} else {
|
|
1211
|
+
await verifyArchiveChecksum(
|
|
1212
|
+
params,
|
|
1213
|
+
source.downloadIdentity,
|
|
1214
|
+
previous.archiveEntryHashes ?? [],
|
|
1215
|
+
params.retainResultArchive
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
return {
|
|
1219
|
+
documentId: params.documentId,
|
|
1220
|
+
revisionKey: params.revisionKey,
|
|
1221
|
+
snapshot: params.snapshot,
|
|
1222
|
+
completed: true,
|
|
1223
|
+
commit: committed
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1180
1227
|
for await (const entry of source.entries) {
|
|
1181
1228
|
if (failure !== void 0) throw failure;
|
|
1182
1229
|
signal.throwIfAborted();
|
|
@@ -1194,7 +1241,12 @@ async function syncRemoteResultArchive(params) {
|
|
|
1194
1241
|
if (startedEntries > 0 && (startedEntries >= maxEntries || Date.now() - startedAt >= deadlineMs)) {
|
|
1195
1242
|
await drainEntries();
|
|
1196
1243
|
await flushProgress(true);
|
|
1197
|
-
return {
|
|
1244
|
+
return {
|
|
1245
|
+
documentId: params.documentId,
|
|
1246
|
+
revisionKey: params.revisionKey,
|
|
1247
|
+
snapshot: params.snapshot,
|
|
1248
|
+
completed: false
|
|
1249
|
+
};
|
|
1198
1250
|
}
|
|
1199
1251
|
if (successfulEntries >= checkpointFileInterval || Date.now() - checkpointAt >= checkpointIntervalMs) {
|
|
1200
1252
|
await drainEntries();
|
|
@@ -1208,9 +1260,16 @@ async function syncRemoteResultArchive(params) {
|
|
|
1208
1260
|
await drainEntries();
|
|
1209
1261
|
await flushProgress(true);
|
|
1210
1262
|
await validateRequiredObjects(params, paths);
|
|
1263
|
+
if (params.snapshot && source.canSkipEntries)
|
|
1264
|
+
if (source.verifyArchiveChecksum) await source.verifyArchiveChecksum(entryHashes);
|
|
1265
|
+
else await verifyArchiveChecksum(params, source.downloadIdentity, entryHashes);
|
|
1211
1266
|
await params.verifyRevision?.();
|
|
1212
1267
|
signal.throwIfAborted();
|
|
1213
1268
|
const commit = {
|
|
1269
|
+
snapshotId: params.snapshot?.snapshotId,
|
|
1270
|
+
jobResultId: params.snapshot?.jobResultId,
|
|
1271
|
+
checksum: params.snapshot?.checksum,
|
|
1272
|
+
stage: params.snapshot?.stage,
|
|
1214
1273
|
version: 1,
|
|
1215
1274
|
documentId: params.documentId,
|
|
1216
1275
|
revisionKey: params.revisionKey,
|
|
@@ -1229,6 +1288,8 @@ async function syncRemoteResultArchive(params) {
|
|
|
1229
1288
|
documentId: params.documentId,
|
|
1230
1289
|
revisionKey: params.revisionKey,
|
|
1231
1290
|
pinnedRevisionKey: params.revisionKey,
|
|
1291
|
+
snapshotChecksum: params.snapshot?.checksum,
|
|
1292
|
+
upstreamJobResultId: params.snapshot?.jobResultId,
|
|
1232
1293
|
archiveIdentity: source.identity,
|
|
1233
1294
|
archiveDownloadIdentity,
|
|
1234
1295
|
archiveCacheBypassed,
|
|
@@ -1249,6 +1310,7 @@ async function syncRemoteResultArchive(params) {
|
|
|
1249
1310
|
documentId: params.documentId,
|
|
1250
1311
|
revisionKey: params.revisionKey,
|
|
1251
1312
|
completed: true,
|
|
1313
|
+
snapshot: params.snapshot,
|
|
1252
1314
|
commit
|
|
1253
1315
|
};
|
|
1254
1316
|
} catch (error) {
|
|
@@ -1264,6 +1326,8 @@ async function syncRemoteResultArchive(params) {
|
|
|
1264
1326
|
documentId: params.documentId,
|
|
1265
1327
|
revisionKey: params.revisionKey,
|
|
1266
1328
|
pinnedRevisionKey: params.revisionKey,
|
|
1329
|
+
snapshotChecksum: params.snapshot?.checksum,
|
|
1330
|
+
upstreamJobResultId: params.snapshot?.jobResultId,
|
|
1267
1331
|
archiveIdentity: source.identity,
|
|
1268
1332
|
archiveDownloadIdentity,
|
|
1269
1333
|
archiveEntryHashes: entryHashes.slice(0, checkpointEntry),
|
|
@@ -1305,7 +1369,7 @@ function validateArchiveSyncLimits(limits) {
|
|
|
1305
1369
|
}
|
|
1306
1370
|
}
|
|
1307
1371
|
async function copyEntry(params, entry, isVerification = false) {
|
|
1308
|
-
const hash = (0,
|
|
1372
|
+
const hash = (0, import_crypto3.createHash)("sha256");
|
|
1309
1373
|
const body = entry.read().pipeThrough(
|
|
1310
1374
|
new TransformStream({
|
|
1311
1375
|
transform(chunk, controller) {
|
|
@@ -1328,7 +1392,7 @@ async function copyEntry(params, entry, isVerification = false) {
|
|
|
1328
1392
|
signal: params.signal
|
|
1329
1393
|
});
|
|
1330
1394
|
await entry.waitForRead?.();
|
|
1331
|
-
return (0,
|
|
1395
|
+
return (0, import_crypto3.createHash)("sha256").update(entry.path).update(hash.digest()).digest("hex");
|
|
1332
1396
|
}
|
|
1333
1397
|
async function openArchive(params, previous) {
|
|
1334
1398
|
const prepared = params.cacheResultArchive ? await prepareCachedArchive({
|
|
@@ -1353,6 +1417,8 @@ async function persistArchiveCacheBypass(params, previous) {
|
|
|
1353
1417
|
documentId: params.documentId,
|
|
1354
1418
|
revisionKey: params.revisionKey,
|
|
1355
1419
|
pinnedRevisionKey: params.revisionKey,
|
|
1420
|
+
snapshotChecksum: params.snapshot?.checksum,
|
|
1421
|
+
upstreamJobResultId: params.snapshot?.jobResultId,
|
|
1356
1422
|
archiveDownloadIdentity: previous?.archiveDownloadIdentity,
|
|
1357
1423
|
archiveCacheBypassed: true,
|
|
1358
1424
|
status: "running",
|
|
@@ -1385,43 +1451,32 @@ async function openArchiveTransport(params, previous, prepared) {
|
|
|
1385
1451
|
);
|
|
1386
1452
|
return { ...source, archiveCacheBypassed };
|
|
1387
1453
|
}
|
|
1388
|
-
const rangeController = new AbortController();
|
|
1389
|
-
const rangeSignal = params.signal ? AbortSignal.any([params.signal, rangeController.signal]) : rangeController.signal;
|
|
1390
1454
|
const options = {
|
|
1391
1455
|
forceRangeRequests: true,
|
|
1392
1456
|
preventHeadRequest: true,
|
|
1393
1457
|
// Cached reads already pin a strong ETag and exact lengths; CDN timestamps can drift.
|
|
1394
1458
|
checkResourceChanges: !prepared?.isCached,
|
|
1395
1459
|
fetch: async (input, init) => {
|
|
1460
|
+
let response;
|
|
1396
1461
|
try {
|
|
1397
|
-
|
|
1398
|
-
try {
|
|
1399
|
-
response = await fetch(input, { ...init, signal: rangeSignal });
|
|
1400
|
-
} catch (error) {
|
|
1401
|
-
rangeSignal.throwIfAborted();
|
|
1402
|
-
if (prepared?.isCached)
|
|
1403
|
-
throw new ArchiveCacheTransportError(
|
|
1404
|
-
"Parsed result archive cache transport unavailable."
|
|
1405
|
-
);
|
|
1406
|
-
throw error;
|
|
1407
|
-
}
|
|
1408
|
-
if (prepared?.isCached) return prepared.validateResponse(response);
|
|
1409
|
-
if (response.status !== 206) {
|
|
1410
|
-
const error = new Error(
|
|
1411
|
-
`Parsed result Range request failed (HTTP ${response.status}).`
|
|
1412
|
-
);
|
|
1413
|
-
rangeController.abort(error);
|
|
1414
|
-
await response.body?.cancel(error).catch(() => void 0);
|
|
1415
|
-
throw error;
|
|
1416
|
-
}
|
|
1417
|
-
return prepared ? prepared.validateResponse(response) : response;
|
|
1462
|
+
response = await fetch(input, { ...init, signal: params.signal });
|
|
1418
1463
|
} catch (error) {
|
|
1419
|
-
|
|
1464
|
+
params.signal?.throwIfAborted();
|
|
1465
|
+
if (prepared?.isCached)
|
|
1466
|
+
throw new ArchiveCacheTransportError(
|
|
1467
|
+
"Parsed result archive cache transport unavailable."
|
|
1468
|
+
);
|
|
1420
1469
|
throw error;
|
|
1421
1470
|
}
|
|
1471
|
+
if (prepared?.isCached) return prepared.validateResponse(response);
|
|
1472
|
+
if (response.status !== 206) {
|
|
1473
|
+
await response.body?.cancel();
|
|
1474
|
+
throw new Error(`Parsed result Range request failed (HTTP ${response.status}).`);
|
|
1475
|
+
}
|
|
1476
|
+
return prepared ? prepared.validateResponse(response) : response;
|
|
1422
1477
|
}
|
|
1423
1478
|
};
|
|
1424
|
-
const reader = new import_zip2.ZipReader(new
|
|
1479
|
+
const reader = new import_zip2.ZipReader(new import_zip2.HttpRangeReader(resultUrl, options), {
|
|
1425
1480
|
useWebWorkers: false,
|
|
1426
1481
|
checkCrc32: true
|
|
1427
1482
|
});
|
|
@@ -1431,17 +1486,23 @@ async function openArchiveTransport(params, previous, prepared) {
|
|
|
1431
1486
|
const files = entries.filter(
|
|
1432
1487
|
(entry) => !entry.directory && isSnapshotPath(entry.filename)
|
|
1433
1488
|
);
|
|
1434
|
-
const archiveIdentity = (0,
|
|
1489
|
+
const archiveIdentity = (0, import_crypto3.createHash)("sha256").update(identity).update(
|
|
1435
1490
|
JSON.stringify(
|
|
1436
1491
|
files.map((entry) => [entry.filename, entry.uncompressedSize, entry.signature])
|
|
1437
1492
|
)
|
|
1438
1493
|
).digest("hex");
|
|
1439
1494
|
return {
|
|
1440
|
-
signal: rangeSignal,
|
|
1441
1495
|
identity: previous?.archiveIdentity === identity ? identity : archiveIdentity,
|
|
1442
1496
|
downloadIdentity: identity,
|
|
1443
1497
|
archiveCacheBypassed,
|
|
1444
1498
|
isCached: prepared?.isCached,
|
|
1499
|
+
verifyArchiveChecksum: prepared?.isCached ? (entryHashes) => verifyArchiveChecksum(
|
|
1500
|
+
{ ...params, resultUrl },
|
|
1501
|
+
identity,
|
|
1502
|
+
entryHashes,
|
|
1503
|
+
false,
|
|
1504
|
+
prepared.validateResponse
|
|
1505
|
+
) : void 0,
|
|
1445
1506
|
canSkipEntries: previous?.archiveIdentity !== identity,
|
|
1446
1507
|
entries: (function* () {
|
|
1447
1508
|
for (const entry of files) {
|
|
@@ -1450,7 +1511,7 @@ async function openArchiveTransport(params, previous, prepared) {
|
|
|
1450
1511
|
path: normalizeResultPath(entry.filename),
|
|
1451
1512
|
contentLength: entry.uncompressedSize,
|
|
1452
1513
|
read: () => {
|
|
1453
|
-
entryRead = readRangeEntry(entry,
|
|
1514
|
+
entryRead = readRangeEntry(entry, params.signal, producers);
|
|
1454
1515
|
return entryRead.body;
|
|
1455
1516
|
},
|
|
1456
1517
|
waitForRead: () => entryRead?.completed ?? Promise.resolve()
|
|
@@ -1463,7 +1524,6 @@ async function openArchiveTransport(params, previous, prepared) {
|
|
|
1463
1524
|
}
|
|
1464
1525
|
};
|
|
1465
1526
|
} catch (error) {
|
|
1466
|
-
rangeController.abort(error);
|
|
1467
1527
|
await reader.close();
|
|
1468
1528
|
throw error;
|
|
1469
1529
|
}
|
|
@@ -1475,16 +1535,10 @@ function readRangeEntry(entry, signal, producers) {
|
|
|
1475
1535
|
streamController = controller;
|
|
1476
1536
|
}
|
|
1477
1537
|
});
|
|
1478
|
-
const cancelOutput = () => {
|
|
1479
|
-
streamController?.error(signal?.reason);
|
|
1480
|
-
};
|
|
1481
|
-
signal?.addEventListener("abort", cancelOutput, { once: true });
|
|
1482
|
-
if (signal?.aborted) cancelOutput();
|
|
1483
1538
|
const completed = entry.getData(stream.writable, { signal, checkCrc32: true, useWebWorkers: false }).then(() => void 0);
|
|
1484
1539
|
const producer = completed.catch((error) => {
|
|
1485
1540
|
streamController?.error(error);
|
|
1486
1541
|
}).finally(() => {
|
|
1487
|
-
signal?.removeEventListener("abort", cancelOutput);
|
|
1488
1542
|
producers.delete(producer);
|
|
1489
1543
|
});
|
|
1490
1544
|
producers.add(producer);
|
|
@@ -1521,6 +1575,9 @@ async function validateRequiredObjects(params, paths) {
|
|
|
1521
1575
|
const manifestJobId = manifest.job_id ?? manifest.jobId;
|
|
1522
1576
|
if (manifestJobId !== params.jobId)
|
|
1523
1577
|
throw new Error("Parsed result manifest job identity differs from the pinned job.");
|
|
1578
|
+
const manifestRevision = manifest.job_result_id ?? manifest.jobResultId;
|
|
1579
|
+
if (params.snapshot && manifestRevision !== void 0 && manifestRevision !== params.snapshot.jobResultId)
|
|
1580
|
+
throw new Error("Parsed result manifest revision differs from the snapshot upstream revision.");
|
|
1524
1581
|
const chunks = Array.isArray(payload) ? payload : isRecord2(payload) ? payload.chunks : void 0;
|
|
1525
1582
|
if (!Array.isArray(chunks)) throw new Error("Invalid parsed result chunks.json.");
|
|
1526
1583
|
if (isRecord2(manifest.files)) {
|
|
@@ -1545,7 +1602,49 @@ async function validateRequiredObjects(params, paths) {
|
|
|
1545
1602
|
for (const path4 of required)
|
|
1546
1603
|
if (!paths.has(path4)) throw new Error(`Required parsed result file missing: ${path4}`);
|
|
1547
1604
|
}
|
|
1605
|
+
async function verifyArchiveChecksum(params, identity, expectedEntryHashes, retainResultArchive = false, validateResponse) {
|
|
1606
|
+
const { openSequentialArchive: openSequentialArchive2 } = await Promise.resolve().then(() => (init_sequential_parsed_document_archive(), sequential_parsed_document_archive_exports));
|
|
1607
|
+
const source = await openSequentialArchive2(params, identity, validateResponse);
|
|
1608
|
+
let entryIndex = 0;
|
|
1609
|
+
try {
|
|
1610
|
+
for await (const entry of source.entries) {
|
|
1611
|
+
const hash = (0, import_crypto3.createHash)("sha256");
|
|
1612
|
+
for await (const part of entry.read()) {
|
|
1613
|
+
params.signal?.throwIfAborted();
|
|
1614
|
+
hash.update(part);
|
|
1615
|
+
}
|
|
1616
|
+
const entryHash = (0, import_crypto3.createHash)("sha256").update(entry.path).update(hash.digest()).digest("hex");
|
|
1617
|
+
if (expectedEntryHashes[entryIndex] !== entryHash)
|
|
1618
|
+
throw new Error(
|
|
1619
|
+
"Parsed snapshot archive entry changed between extraction and checksum verification."
|
|
1620
|
+
);
|
|
1621
|
+
entryIndex += 1;
|
|
1622
|
+
}
|
|
1623
|
+
if (entryIndex !== expectedEntryHashes.length)
|
|
1624
|
+
throw new Error("Parsed snapshot archive entry count changed during checksum verification.");
|
|
1625
|
+
if (retainResultArchive && source.readArchive) {
|
|
1626
|
+
try {
|
|
1627
|
+
await writeStream(params.storage, {
|
|
1628
|
+
documentId: params.documentId,
|
|
1629
|
+
revisionKey: params.revisionKey,
|
|
1630
|
+
path: "result.zip",
|
|
1631
|
+
body: source.readArchive(),
|
|
1632
|
+
contentType: "application/zip",
|
|
1633
|
+
signal: params.signal
|
|
1634
|
+
});
|
|
1635
|
+
} catch {
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
} finally {
|
|
1639
|
+
await source.close();
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1548
1642
|
async function retainArchive(params, expectedIdentity) {
|
|
1643
|
+
if (params.snapshot) {
|
|
1644
|
+
const progress = await params.storage.readSyncProgress(params);
|
|
1645
|
+
await verifyArchiveChecksum(params, expectedIdentity, progress?.archiveEntryHashes ?? [], true);
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1549
1648
|
const response = await fetch(params.resultUrl, { signal: params.signal });
|
|
1550
1649
|
if (!response.ok || !response.body)
|
|
1551
1650
|
throw new Error(`Optional archive download failed (HTTP ${response.status}).`);
|
|
@@ -1570,7 +1669,7 @@ function isRecord2(value) {
|
|
|
1570
1669
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1571
1670
|
}
|
|
1572
1671
|
function createDownloadIdentity(jobId, response) {
|
|
1573
|
-
return (0,
|
|
1672
|
+
return (0, import_crypto3.createHash)("sha256").update(
|
|
1574
1673
|
JSON.stringify([
|
|
1575
1674
|
jobId,
|
|
1576
1675
|
response.headers.get("etag"),
|
|
@@ -1579,60 +1678,16 @@ function createDownloadIdentity(jobId, response) {
|
|
|
1579
1678
|
])
|
|
1580
1679
|
).digest("hex");
|
|
1581
1680
|
}
|
|
1582
|
-
var
|
|
1681
|
+
var import_crypto3, import_zip2;
|
|
1583
1682
|
var init_streamed_parsed_document_sync = __esm({
|
|
1584
1683
|
"src/storage/streamed-parsed-document-sync.ts"() {
|
|
1585
1684
|
"use strict";
|
|
1586
|
-
|
|
1685
|
+
import_crypto3 = require("crypto");
|
|
1587
1686
|
init_errors();
|
|
1588
1687
|
init_archive_cache_transport_error();
|
|
1589
1688
|
init_parsed_document_archive_cache();
|
|
1590
1689
|
import_zip2 = require("@zip.js/zip.js");
|
|
1591
1690
|
init_parsed_document_storage();
|
|
1592
|
-
AbortableHttpRangeReader = class extends import_zip2.HttpRangeReader {
|
|
1593
|
-
constructor(url, options, controller) {
|
|
1594
|
-
super(url, options);
|
|
1595
|
-
this.controller = controller;
|
|
1596
|
-
}
|
|
1597
|
-
controller;
|
|
1598
|
-
createReadable(options) {
|
|
1599
|
-
const reader = super.createReadable(options).getReader();
|
|
1600
|
-
const cancellation = this.controller;
|
|
1601
|
-
return new ReadableStream(
|
|
1602
|
-
{
|
|
1603
|
-
async pull(output) {
|
|
1604
|
-
try {
|
|
1605
|
-
const chunk = await reader.read();
|
|
1606
|
-
if (chunk.done) {
|
|
1607
|
-
reader.releaseLock();
|
|
1608
|
-
output.close();
|
|
1609
|
-
} else output.enqueue(chunk.value);
|
|
1610
|
-
} catch (error) {
|
|
1611
|
-
cancellation.abort(error);
|
|
1612
|
-
reader.releaseLock();
|
|
1613
|
-
output.error(error);
|
|
1614
|
-
}
|
|
1615
|
-
},
|
|
1616
|
-
async cancel(reason) {
|
|
1617
|
-
try {
|
|
1618
|
-
await reader.cancel(reason);
|
|
1619
|
-
} finally {
|
|
1620
|
-
reader.releaseLock();
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
},
|
|
1624
|
-
{ highWaterMark: 0 }
|
|
1625
|
-
);
|
|
1626
|
-
}
|
|
1627
|
-
async readUint8Array(index, length) {
|
|
1628
|
-
try {
|
|
1629
|
-
return await super.readUint8Array(index, length);
|
|
1630
|
-
} catch (error) {
|
|
1631
|
-
this.controller.abort(error);
|
|
1632
|
-
throw error;
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1636
1691
|
}
|
|
1637
1692
|
});
|
|
1638
1693
|
|
|
@@ -2707,6 +2762,12 @@ var Jobs = class extends BaseResource {
|
|
|
2707
2762
|
enrichJobResult(jobResult);
|
|
2708
2763
|
return jobResult;
|
|
2709
2764
|
}
|
|
2765
|
+
/** Renew unfinished enhancement without creating another ingestion. */
|
|
2766
|
+
async retryEnhancement(jobId) {
|
|
2767
|
+
return this.httpClient.post(
|
|
2768
|
+
this.endpoint(`/jobs/${jobId}/enrichment/retry`)
|
|
2769
|
+
);
|
|
2770
|
+
}
|
|
2710
2771
|
/**
|
|
2711
2772
|
* Upload file for a job
|
|
2712
2773
|
*/
|
|
@@ -2917,7 +2978,7 @@ var Documents = class extends BaseResource {
|
|
|
2917
2978
|
};
|
|
2918
2979
|
|
|
2919
2980
|
// src/knowledge/knowledge.ts
|
|
2920
|
-
var
|
|
2981
|
+
var import_crypto5 = require("crypto");
|
|
2921
2982
|
init_errors();
|
|
2922
2983
|
|
|
2923
2984
|
// src/storage/asset-storage.ts
|
|
@@ -3188,7 +3249,7 @@ init_utils();
|
|
|
3188
3249
|
init_streamed_parsed_document_sync();
|
|
3189
3250
|
|
|
3190
3251
|
// src/knowledge/local-store.ts
|
|
3191
|
-
var
|
|
3252
|
+
var import_crypto4 = require("crypto");
|
|
3192
3253
|
var import_os2 = __toESM(require("os"));
|
|
3193
3254
|
var import_fs3 = require("fs");
|
|
3194
3255
|
var import_path3 = __toESM(require("path"));
|
|
@@ -3372,7 +3433,7 @@ function isPathInsideDirectory(targetPath, parentDirectory) {
|
|
|
3372
3433
|
return relativePath.length === 0 || !relativePath.startsWith("..") && !import_path3.default.isAbsolute(relativePath);
|
|
3373
3434
|
}
|
|
3374
3435
|
function createLocalDocumentId(result) {
|
|
3375
|
-
const hash = (0,
|
|
3436
|
+
const hash = (0, import_crypto4.createHash)("sha256").update(result.jobId).update("\0").update(result.manifest.sourceFileName).digest("hex").slice(0, 16);
|
|
3376
3437
|
return `local_${hash}`;
|
|
3377
3438
|
}
|
|
3378
3439
|
function countChunkTypes(result) {
|
|
@@ -3409,6 +3470,21 @@ function isMissingFileError2(error) {
|
|
|
3409
3470
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
3410
3471
|
}
|
|
3411
3472
|
|
|
3473
|
+
// src/knowledge/serialize-snapshot-publication.ts
|
|
3474
|
+
var pendingPublications = /* @__PURE__ */ new WeakMap();
|
|
3475
|
+
async function serializeSnapshotPublication(storage, documentId, publish) {
|
|
3476
|
+
const publications = pendingPublications.get(storage) ?? /* @__PURE__ */ new Map();
|
|
3477
|
+
pendingPublications.set(storage, publications);
|
|
3478
|
+
const previous = publications.get(documentId) ?? Promise.resolve();
|
|
3479
|
+
const current = previous.catch(() => void 0).then(publish);
|
|
3480
|
+
publications.set(documentId, current);
|
|
3481
|
+
try {
|
|
3482
|
+
return await current;
|
|
3483
|
+
} finally {
|
|
3484
|
+
if (publications.get(documentId) === current) publications.delete(documentId);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3412
3488
|
// src/knowledge/knowledge.ts
|
|
3413
3489
|
var DEFAULT_READ_LIMIT = 12;
|
|
3414
3490
|
var MAX_READ_LIMIT = 40;
|
|
@@ -3526,11 +3602,37 @@ var Knowledge = class _Knowledge {
|
|
|
3526
3602
|
return this.importJobResult(params);
|
|
3527
3603
|
}
|
|
3528
3604
|
async syncParsedDocument(params) {
|
|
3605
|
+
const response = await this.performParsedDocumentSync(params);
|
|
3606
|
+
if (response.completed && !response.snapshot) {
|
|
3607
|
+
const storage = this.requireParsedStorageConfig().storage;
|
|
3608
|
+
await serializeSnapshotPublication(storage, response.documentId, async () => {
|
|
3609
|
+
if (params.documentId || params.jobId) {
|
|
3610
|
+
const current = await this.client.documents.listChunks(
|
|
3611
|
+
response.documentId,
|
|
3612
|
+
{ page: 1, pageSize: 1 }
|
|
3613
|
+
);
|
|
3614
|
+
if (params.jobId && current.jobId !== params.jobId) return;
|
|
3615
|
+
if (!params.jobId && current.jobResultId !== response.revisionKey) return;
|
|
3616
|
+
}
|
|
3617
|
+
await storage.writeObject({
|
|
3618
|
+
documentId: response.documentId,
|
|
3619
|
+
revisionKey: "current",
|
|
3620
|
+
path: ".knowhere-sdk/current-snapshot.json",
|
|
3621
|
+
body: Buffer.from(
|
|
3622
|
+
JSON.stringify({ snapshotId: response.revisionKey, jobResultId: response.revisionKey })
|
|
3623
|
+
),
|
|
3624
|
+
contentType: "application/json; charset=utf-8"
|
|
3625
|
+
});
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
return response;
|
|
3629
|
+
}
|
|
3630
|
+
async performParsedDocumentSync(params) {
|
|
3529
3631
|
params.signal?.throwIfAborted();
|
|
3530
3632
|
const normalized = normalizeDocumentReference(params);
|
|
3531
3633
|
const parsedStorageConfig = this.requireParsedStorageConfig();
|
|
3532
3634
|
validateArchiveSyncLimits(parsedStorageConfig.limits);
|
|
3533
|
-
if (normalized.documentId) {
|
|
3635
|
+
if (normalized.documentId && !normalized.jobId) {
|
|
3534
3636
|
return this.syncRemoteDocumentToParsedStorage({
|
|
3535
3637
|
documentId: normalized.documentId,
|
|
3536
3638
|
revisionKey: params.revisionKey,
|
|
@@ -3542,6 +3644,25 @@ var Knowledge = class _Knowledge {
|
|
|
3542
3644
|
}
|
|
3543
3645
|
if (normalized.jobId) {
|
|
3544
3646
|
const job = await this.client.jobs.get(normalized.jobId);
|
|
3647
|
+
if (job.snapshots?.length) {
|
|
3648
|
+
return this.syncJobSnapshot({
|
|
3649
|
+
job,
|
|
3650
|
+
documentId: normalized.documentId ?? job.documentId ?? normalized.localDocumentId ?? normalized.jobId,
|
|
3651
|
+
revisionKey: params.revisionKey,
|
|
3652
|
+
parsedStorageConfig,
|
|
3653
|
+
signal: params.signal,
|
|
3654
|
+
retainResultArchive: params.retainResultArchive
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
if (normalized.documentId) {
|
|
3658
|
+
return this.syncRemoteDocumentToParsedStorage({
|
|
3659
|
+
documentId: normalized.documentId,
|
|
3660
|
+
revisionKey: params.revisionKey,
|
|
3661
|
+
parsedStorageConfig,
|
|
3662
|
+
signal: params.signal,
|
|
3663
|
+
retainResultArchive: params.retainResultArchive
|
|
3664
|
+
});
|
|
3665
|
+
}
|
|
3545
3666
|
if (!job.isDone || !job.resultUrl)
|
|
3546
3667
|
throw new ValidationError("Completed job result archive is unavailable");
|
|
3547
3668
|
return syncRemoteResultArchive({
|
|
@@ -3573,6 +3694,89 @@ var Knowledge = class _Knowledge {
|
|
|
3573
3694
|
commit: synced.commit
|
|
3574
3695
|
};
|
|
3575
3696
|
}
|
|
3697
|
+
async syncJobSnapshot(params) {
|
|
3698
|
+
const snapshotId = params.revisionKey ?? params.job.currentSnapshotId;
|
|
3699
|
+
const snapshot = params.job.snapshots?.find(
|
|
3700
|
+
(candidate) => candidate.snapshotId === snapshotId
|
|
3701
|
+
);
|
|
3702
|
+
if (!params.job.isDone || !snapshot)
|
|
3703
|
+
throw new ValidationError("Requested immutable parsed snapshot is unavailable");
|
|
3704
|
+
if (snapshot.jobId !== params.job.jobId || snapshot.documentId !== params.documentId)
|
|
3705
|
+
throw new ValidationError(
|
|
3706
|
+
"Parsed snapshot document or job identity differs from the request"
|
|
3707
|
+
);
|
|
3708
|
+
const response = await syncRemoteResultArchive({
|
|
3709
|
+
documentId: params.documentId,
|
|
3710
|
+
revisionKey: snapshot.snapshotId,
|
|
3711
|
+
jobId: snapshot.jobId,
|
|
3712
|
+
snapshot,
|
|
3713
|
+
resultUrl: snapshot.resultUrl,
|
|
3714
|
+
storage: params.parsedStorageConfig.storage,
|
|
3715
|
+
limits: params.parsedStorageConfig.limits,
|
|
3716
|
+
signal: params.signal,
|
|
3717
|
+
retainResultArchive: params.retainResultArchive
|
|
3718
|
+
});
|
|
3719
|
+
if (response.completed && snapshot.snapshotId === params.job.currentSnapshotId) {
|
|
3720
|
+
await serializeSnapshotPublication(
|
|
3721
|
+
params.parsedStorageConfig.storage,
|
|
3722
|
+
params.documentId,
|
|
3723
|
+
async () => {
|
|
3724
|
+
const current = await this.client.documents.listChunks(
|
|
3725
|
+
params.documentId,
|
|
3726
|
+
{ page: 1, pageSize: 1 }
|
|
3727
|
+
);
|
|
3728
|
+
if (current.jobResultId !== snapshot.jobResultId) return;
|
|
3729
|
+
const pointer = Buffer.from(
|
|
3730
|
+
JSON.stringify({
|
|
3731
|
+
snapshotId: snapshot.snapshotId,
|
|
3732
|
+
jobResultId: snapshot.jobResultId
|
|
3733
|
+
})
|
|
3734
|
+
);
|
|
3735
|
+
if (snapshot.stage === "final") {
|
|
3736
|
+
await params.parsedStorageConfig.storage.writeObject({
|
|
3737
|
+
documentId: params.documentId,
|
|
3738
|
+
revisionKey: "current",
|
|
3739
|
+
path: ".knowhere-sdk/final-snapshot.json",
|
|
3740
|
+
body: pointer,
|
|
3741
|
+
contentType: "application/json; charset=utf-8"
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
await params.parsedStorageConfig.storage.writeObject({
|
|
3745
|
+
documentId: params.documentId,
|
|
3746
|
+
revisionKey: "current",
|
|
3747
|
+
path: ".knowhere-sdk/current-snapshot.json",
|
|
3748
|
+
body: pointer,
|
|
3749
|
+
contentType: "application/json; charset=utf-8"
|
|
3750
|
+
});
|
|
3751
|
+
}
|
|
3752
|
+
);
|
|
3753
|
+
}
|
|
3754
|
+
return response;
|
|
3755
|
+
}
|
|
3756
|
+
async resolveStoredSnapshotKey(documentId) {
|
|
3757
|
+
const object = await this.parsedStorageConfig?.storage.readObject({
|
|
3758
|
+
documentId,
|
|
3759
|
+
revisionKey: "current",
|
|
3760
|
+
path: ".knowhere-sdk/current-snapshot.json"
|
|
3761
|
+
});
|
|
3762
|
+
if (!object) return void 0;
|
|
3763
|
+
const payload = JSON.parse(Buffer.from(object.body).toString("utf8"));
|
|
3764
|
+
if (typeof payload !== "object" || payload === null || !("snapshotId" in payload))
|
|
3765
|
+
throw new ValidationError("Invalid current parsed snapshot pointer");
|
|
3766
|
+
if (typeof payload.snapshotId !== "string")
|
|
3767
|
+
throw new ValidationError("Invalid current parsed snapshot identity");
|
|
3768
|
+
const finalObject = await this.parsedStorageConfig?.storage.readObject({
|
|
3769
|
+
documentId,
|
|
3770
|
+
revisionKey: "current",
|
|
3771
|
+
path: ".knowhere-sdk/final-snapshot.json"
|
|
3772
|
+
});
|
|
3773
|
+
if (finalObject && "jobResultId" in payload) {
|
|
3774
|
+
const finalPayload = JSON.parse(Buffer.from(finalObject.body).toString("utf8"));
|
|
3775
|
+
if (typeof finalPayload === "object" && finalPayload !== null && "snapshotId" in finalPayload && typeof finalPayload.snapshotId === "string" && "jobResultId" in finalPayload && finalPayload.jobResultId === payload.jobResultId)
|
|
3776
|
+
return finalPayload.snapshotId;
|
|
3777
|
+
}
|
|
3778
|
+
return payload.snapshotId;
|
|
3779
|
+
}
|
|
3576
3780
|
async syncParseResultToConfiguredStorage(result) {
|
|
3577
3781
|
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3578
3782
|
if (!parsedStorageConfig) {
|
|
@@ -3808,7 +4012,7 @@ var Knowledge = class _Knowledge {
|
|
|
3808
4012
|
throw new Error(`Local Knowhere document not found: ${normalized.localDocumentId}`);
|
|
3809
4013
|
}
|
|
3810
4014
|
async readRemoteDocumentChunks(documentId, params) {
|
|
3811
|
-
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
4015
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
|
|
3812
4016
|
if (explicitRevisionKey) {
|
|
3813
4017
|
const stored2 = await this.tryReadChunksFromParsedStorage({
|
|
3814
4018
|
documentId,
|
|
@@ -3818,6 +4022,8 @@ var Knowledge = class _Knowledge {
|
|
|
3818
4022
|
if (stored2) {
|
|
3819
4023
|
return stored2;
|
|
3820
4024
|
}
|
|
4025
|
+
if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
|
|
4026
|
+
throw new ValidationError("Requested parsed snapshot is not committed locally");
|
|
3821
4027
|
}
|
|
3822
4028
|
if (hasScanReadFilters(params)) {
|
|
3823
4029
|
return this.scanRemoteDocumentChunks(documentId, params);
|
|
@@ -3830,6 +4036,8 @@ var Knowledge = class _Knowledge {
|
|
|
3830
4036
|
includeAssetUrls: true
|
|
3831
4037
|
});
|
|
3832
4038
|
const revisionKey = getRemoteRevisionKey(remotePage);
|
|
4039
|
+
if (params.revisionKey && params.revisionKey !== revisionKey)
|
|
4040
|
+
throw new ValidationError("Requested parsed snapshot revision is not committed locally");
|
|
3833
4041
|
const stored = await this.tryReadChunksFromParsedStorage({
|
|
3834
4042
|
documentId,
|
|
3835
4043
|
revisionKey,
|
|
@@ -3854,7 +4062,7 @@ var Knowledge = class _Knowledge {
|
|
|
3854
4062
|
};
|
|
3855
4063
|
}
|
|
3856
4064
|
async scanRemoteDocumentChunks(documentId, params) {
|
|
3857
|
-
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
4065
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
|
|
3858
4066
|
if (explicitRevisionKey) {
|
|
3859
4067
|
const stored2 = await this.tryReadChunksFromParsedStorage({
|
|
3860
4068
|
documentId,
|
|
@@ -3864,6 +4072,8 @@ var Knowledge = class _Knowledge {
|
|
|
3864
4072
|
if (stored2) {
|
|
3865
4073
|
return stored2;
|
|
3866
4074
|
}
|
|
4075
|
+
if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
|
|
4076
|
+
throw new ValidationError("Requested parsed snapshot is not committed locally");
|
|
3867
4077
|
}
|
|
3868
4078
|
const limit = clampLimit(params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
|
|
3869
4079
|
const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
|
|
@@ -3874,6 +4084,8 @@ var Knowledge = class _Knowledge {
|
|
|
3874
4084
|
includeAssetUrls: true
|
|
3875
4085
|
});
|
|
3876
4086
|
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
4087
|
+
if (params.revisionKey && params.revisionKey !== revisionKey)
|
|
4088
|
+
throw new ValidationError("Requested parsed snapshot revision is not committed locally");
|
|
3877
4089
|
const stored = await this.tryReadChunksFromParsedStorage({
|
|
3878
4090
|
documentId,
|
|
3879
4091
|
revisionKey,
|
|
@@ -3916,7 +4128,7 @@ var Knowledge = class _Knowledge {
|
|
|
3916
4128
|
};
|
|
3917
4129
|
}
|
|
3918
4130
|
async grepRemoteDocumentChunks(documentId, params) {
|
|
3919
|
-
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
4131
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
|
|
3920
4132
|
if (explicitRevisionKey && !params.continuationCursor) {
|
|
3921
4133
|
const stored2 = await this.tryGrepChunksFromParsedStorage({
|
|
3922
4134
|
documentId,
|
|
@@ -3926,6 +4138,8 @@ var Knowledge = class _Knowledge {
|
|
|
3926
4138
|
if (stored2) {
|
|
3927
4139
|
return stored2;
|
|
3928
4140
|
}
|
|
4141
|
+
if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
|
|
4142
|
+
throw new ValidationError("Requested parsed snapshot is not committed locally");
|
|
3929
4143
|
}
|
|
3930
4144
|
const maxResults = clampLimit(params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
3931
4145
|
const contextChars = params.contextChars ?? DEFAULT_CONTEXT_CHARS;
|
|
@@ -3940,6 +4154,8 @@ var Knowledge = class _Knowledge {
|
|
|
3940
4154
|
includeAssetUrls: true
|
|
3941
4155
|
});
|
|
3942
4156
|
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
4157
|
+
if (params.revisionKey && params.revisionKey !== revisionKey)
|
|
4158
|
+
throw new ValidationError("Requested parsed snapshot revision is not committed locally");
|
|
3943
4159
|
const stored = startPage === 1 ? await this.tryGrepChunksFromParsedStorage({
|
|
3944
4160
|
documentId,
|
|
3945
4161
|
revisionKey,
|
|
@@ -4025,11 +4241,14 @@ var Knowledge = class _Knowledge {
|
|
|
4025
4241
|
}
|
|
4026
4242
|
}
|
|
4027
4243
|
async getRemoteDocumentOutline(documentId, revisionKeyOverride) {
|
|
4028
|
-
|
|
4029
|
-
|
|
4244
|
+
const storedRevisionKey = revisionKeyOverride ?? await this.resolveStoredSnapshotKey(documentId);
|
|
4245
|
+
if (storedRevisionKey) {
|
|
4246
|
+
const stored2 = await this.tryReadOutlineFromParsedStorage(documentId, storedRevisionKey);
|
|
4030
4247
|
if (stored2) {
|
|
4031
4248
|
return stored2;
|
|
4032
4249
|
}
|
|
4250
|
+
if (revisionKeyOverride && await this.resolveStoredSnapshotKey(documentId))
|
|
4251
|
+
throw new ValidationError("Requested parsed snapshot is not committed locally");
|
|
4033
4252
|
}
|
|
4034
4253
|
const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
|
|
4035
4254
|
const firstPage = await this.client.documents.listChunks(documentId, {
|
|
@@ -4038,6 +4257,8 @@ var Knowledge = class _Knowledge {
|
|
|
4038
4257
|
includeAssetUrls: true
|
|
4039
4258
|
});
|
|
4040
4259
|
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
4260
|
+
if (revisionKeyOverride && revisionKeyOverride !== revisionKey)
|
|
4261
|
+
throw new ValidationError("Requested parsed snapshot revision is not committed locally");
|
|
4041
4262
|
const stored = await this.tryReadOutlineFromParsedStorage(documentId, revisionKey);
|
|
4042
4263
|
if (stored) {
|
|
4043
4264
|
return stored;
|
|
@@ -4246,11 +4467,7 @@ var Knowledge = class _Knowledge {
|
|
|
4246
4467
|
const scheduler = parsedStorageConfig.scheduler ?? defaultParsedStorageScheduler;
|
|
4247
4468
|
const task = async () => {
|
|
4248
4469
|
try {
|
|
4249
|
-
await this.
|
|
4250
|
-
documentId,
|
|
4251
|
-
revisionKey,
|
|
4252
|
-
parsedStorageConfig
|
|
4253
|
-
});
|
|
4470
|
+
await this.syncParsedDocument({ documentId, revisionKey });
|
|
4254
4471
|
} catch (error) {
|
|
4255
4472
|
const previous = await parsedStorageConfig.storage.readSyncProgress({
|
|
4256
4473
|
documentId,
|
|
@@ -4276,14 +4493,25 @@ var Knowledge = class _Knowledge {
|
|
|
4276
4493
|
includeAssetUrls: true
|
|
4277
4494
|
});
|
|
4278
4495
|
const revisionKey = params.revisionKey ?? getRemoteRevisionKey(firstPage);
|
|
4279
|
-
if (params.revisionKey && getRemoteRevisionKey(firstPage) !== params.revisionKey) {
|
|
4280
|
-
throw new ValidationError("Published document revision changed before parsed snapshot sync");
|
|
4281
|
-
}
|
|
4282
4496
|
if (firstPage.jobId) {
|
|
4283
4497
|
const job = await this.client.jobs.get(firstPage.jobId).catch((error) => {
|
|
4284
4498
|
if (error instanceof NotFoundError) return null;
|
|
4285
4499
|
throw error;
|
|
4286
4500
|
});
|
|
4501
|
+
if (job?.snapshots?.length) {
|
|
4502
|
+
const snapshot = job.snapshots.find(
|
|
4503
|
+
(candidate) => candidate.snapshotId === (params.revisionKey ?? job.currentSnapshotId)
|
|
4504
|
+
);
|
|
4505
|
+
if (snapshot && snapshot.jobResultId !== getRemoteRevisionKey(firstPage))
|
|
4506
|
+
throw new ValidationError(
|
|
4507
|
+
"Parsed snapshot upstream revision differs from the published document"
|
|
4508
|
+
);
|
|
4509
|
+
return this.syncJobSnapshot({ ...params, job });
|
|
4510
|
+
}
|
|
4511
|
+
if (params.revisionKey && getRemoteRevisionKey(firstPage) !== params.revisionKey)
|
|
4512
|
+
throw new ValidationError(
|
|
4513
|
+
"Published document revision changed before parsed snapshot sync"
|
|
4514
|
+
);
|
|
4287
4515
|
if (job?.isDone && job.resultUrl) {
|
|
4288
4516
|
if (job.documentId && job.documentId !== params.documentId)
|
|
4289
4517
|
throw new ValidationError("Parsed job document identity differs from requested document");
|
|
@@ -4492,7 +4720,7 @@ function createLocalDocumentIdForRemote(documentId) {
|
|
|
4492
4720
|
if (SAFE_LOCAL_DOCUMENT_ID_PATTERN.test(documentId) && !documentId.includes("..") && !documentId.includes("/") && !documentId.includes("\\")) {
|
|
4493
4721
|
return documentId;
|
|
4494
4722
|
}
|
|
4495
|
-
const hash = (0,
|
|
4723
|
+
const hash = (0, import_crypto5.createHash)("sha256").update(documentId).digest("hex").slice(0, 16);
|
|
4496
4724
|
return `remote_${hash}`;
|
|
4497
4725
|
}
|
|
4498
4726
|
var defaultParsedStorageScheduler = {
|
|
@@ -5450,7 +5678,7 @@ var Knowhere = class {
|
|
|
5450
5678
|
};
|
|
5451
5679
|
|
|
5452
5680
|
// src/storage/disk-parsed-document-storage.ts
|
|
5453
|
-
var
|
|
5681
|
+
var import_crypto6 = require("crypto");
|
|
5454
5682
|
var import_fs4 = require("fs");
|
|
5455
5683
|
var import_path5 = __toESM(require("path"));
|
|
5456
5684
|
var syncProgressPath = ".knowhere-sdk/sync-progress.json";
|
|
@@ -5583,7 +5811,7 @@ function hashPathPart(value) {
|
|
|
5583
5811
|
if (normalized.length === 0) {
|
|
5584
5812
|
throw new Error("Parsed document storage path part must be a non-empty value.");
|
|
5585
5813
|
}
|
|
5586
|
-
return (0,
|
|
5814
|
+
return (0, import_crypto6.createHash)("sha256").update(normalized).digest("hex").slice(0, 24);
|
|
5587
5815
|
}
|
|
5588
5816
|
function normalizeRelativeStoragePath(value) {
|
|
5589
5817
|
const normalized = value.replaceAll("\\", "/").replace(/^\.\/+/, "");
|