@ontos-ai/knowhere-sdk 2.4.2 → 2.5.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/index.js CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -15,17 +18,877 @@ var __copyProps = (to, from, except, desc) => {
15
18
  if (!__hasOwnProp.call(to, key) && key !== except)
16
19
  __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
20
  }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/constants.ts
34
+ var DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_POLL_INTERVAL, MAX_POLL_INTERVAL, DEFAULT_POLL_TIMEOUT, POLL_INTERVAL_INCREASE_THRESHOLD, POLL_INTERVAL_MULTIPLIER, INITIAL_RETRY_DELAY, MAX_RETRY_DELAY, RETRY_DELAY_BASE, RETRYABLE_STATUS_CODES, TERMINAL_JOB_STATUSES, ENV;
35
+ var init_constants = __esm({
36
+ "src/constants.ts"() {
37
+ "use strict";
38
+ DEFAULT_BASE_URL = "https://api.knowhereto.ai";
39
+ DEFAULT_TIMEOUT = 6e4;
40
+ DEFAULT_MAX_RETRIES = 5;
41
+ DEFAULT_POLL_INTERVAL = 1e4;
42
+ MAX_POLL_INTERVAL = 3e4;
43
+ DEFAULT_POLL_TIMEOUT = 18e5;
44
+ POLL_INTERVAL_INCREASE_THRESHOLD = 6e4;
45
+ POLL_INTERVAL_MULTIPLIER = 1.2;
46
+ INITIAL_RETRY_DELAY = 500;
47
+ MAX_RETRY_DELAY = 3e4;
48
+ RETRY_DELAY_BASE = 2;
49
+ RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([409, 429, 502, 503, 504]);
50
+ TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["done", "failed"]);
51
+ ENV = {
52
+ API_KEY: "KNOWHERE_API_KEY",
53
+ BASE_URL: "KNOWHERE_BASE_URL",
54
+ LOG_LEVEL: "KNOWHERE_LOG_LEVEL"
55
+ };
56
+ }
57
+ });
58
+
59
+ // src/errors/base.ts
60
+ var KnowhereError, NetworkError, TimeoutError, PollingTimeoutError, ChecksumError, ValidationError, InvalidStateError;
61
+ var init_base = __esm({
62
+ "src/errors/base.ts"() {
63
+ "use strict";
64
+ KnowhereError = class extends Error {
65
+ constructor(message) {
66
+ super(message);
67
+ this.name = "KnowhereError";
68
+ Object.setPrototypeOf(this, new.target.prototype);
69
+ }
70
+ };
71
+ NetworkError = class extends KnowhereError {
72
+ constructor(message, cause) {
73
+ super(message);
74
+ this.cause = cause;
75
+ this.name = "NetworkError";
76
+ }
77
+ cause;
78
+ };
79
+ TimeoutError = class extends NetworkError {
80
+ constructor(message = "Request timed out") {
81
+ super(message);
82
+ this.name = "TimeoutError";
83
+ }
84
+ };
85
+ PollingTimeoutError = class extends KnowhereError {
86
+ constructor(message = "Polling timed out", elapsedMs) {
87
+ super(message);
88
+ this.elapsedMs = elapsedMs;
89
+ this.name = "PollingTimeoutError";
90
+ }
91
+ elapsedMs;
92
+ };
93
+ ChecksumError = class extends KnowhereError {
94
+ constructor(message = "Checksum verification failed", expected, actual) {
95
+ super(message);
96
+ this.expected = expected;
97
+ this.actual = actual;
98
+ this.name = "ChecksumError";
99
+ }
100
+ expected;
101
+ actual;
102
+ };
103
+ ValidationError = class extends KnowhereError {
104
+ constructor(message) {
105
+ super(message);
106
+ this.name = "ValidationError";
107
+ }
108
+ };
109
+ InvalidStateError = class extends KnowhereError {
110
+ constructor(message) {
111
+ super(message);
112
+ this.name = "InvalidStateError";
113
+ }
114
+ };
115
+ }
116
+ });
117
+
118
+ // src/errors/parsed-document-archive-error.ts
119
+ var ParsedDocumentArchiveError;
120
+ var init_parsed_document_archive_error = __esm({
121
+ "src/errors/parsed-document-archive-error.ts"() {
122
+ "use strict";
123
+ init_base();
124
+ ParsedDocumentArchiveError = class extends KnowhereError {
125
+ code = "ARCHIVE_SPOOL_FAILED";
126
+ cause;
127
+ constructor(cause) {
128
+ super(
129
+ "Parsed result ZIP temporary storage is unavailable. Range-enabled object storage avoids temporary disk usage."
130
+ );
131
+ this.name = "ParsedDocumentArchiveError";
132
+ this.cause = cause;
133
+ }
134
+ };
135
+ }
136
+ });
137
+
138
+ // src/lib/utils.ts
139
+ function sleep(ms) {
140
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
141
+ }
142
+ function snakeToCamel(str) {
143
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
144
+ }
145
+ function camelToSnake(str) {
146
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
147
+ }
148
+ function keysToCamel(obj) {
149
+ if (obj === null || obj === void 0) {
150
+ return obj;
151
+ }
152
+ if (Array.isArray(obj)) {
153
+ return obj.map((item) => keysToCamel(item));
154
+ }
155
+ if (typeof obj === "object" && obj.constructor === Object) {
156
+ const result = {};
157
+ for (const [key, value] of Object.entries(obj)) {
158
+ result[snakeToCamel(key)] = keysToCamel(value);
159
+ }
160
+ return result;
161
+ }
162
+ return obj;
163
+ }
164
+ function keysToSnake(obj) {
165
+ if (obj === null || obj === void 0) {
166
+ return obj;
167
+ }
168
+ if (Array.isArray(obj)) {
169
+ return obj.map((item) => keysToSnake(item));
170
+ }
171
+ if (typeof obj === "object" && obj.constructor === Object) {
172
+ const result = {};
173
+ for (const [key, value] of Object.entries(obj)) {
174
+ result[camelToSnake(key)] = keysToSnake(value);
175
+ }
176
+ return result;
177
+ }
178
+ return obj;
179
+ }
180
+ function parseDates(obj) {
181
+ if (obj === null || obj === void 0) {
182
+ return obj;
183
+ }
184
+ if (Array.isArray(obj)) {
185
+ return obj.map((item) => parseDates(item));
186
+ }
187
+ if (typeof obj === "object" && obj.constructor === Object) {
188
+ const result = {};
189
+ for (const [key, value] of Object.entries(obj)) {
190
+ if ((key.endsWith("At") || key.endsWith("Date")) && typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
191
+ result[key] = new Date(value);
192
+ } else {
193
+ result[key] = parseDates(value);
194
+ }
195
+ }
196
+ return result;
197
+ }
198
+ return obj;
199
+ }
200
+ function isTerminalStatus(status) {
201
+ return TERMINAL_JOB_STATUSES.has(status);
202
+ }
203
+ function enrichJobResult(jobResult) {
204
+ const computedProperties = {};
205
+ if (!Object.getOwnPropertyDescriptor(jobResult, "isTerminal")) {
206
+ computedProperties.isTerminal = {
207
+ get() {
208
+ return isTerminalStatus(this.status);
209
+ },
210
+ enumerable: true,
211
+ configurable: true
212
+ };
213
+ }
214
+ if (!Object.getOwnPropertyDescriptor(jobResult, "isDone")) {
215
+ computedProperties.isDone = {
216
+ get() {
217
+ return this.status === "done";
218
+ },
219
+ enumerable: true,
220
+ configurable: true
221
+ };
222
+ }
223
+ if (!Object.getOwnPropertyDescriptor(jobResult, "isFailed")) {
224
+ computedProperties.isFailed = {
225
+ get() {
226
+ return this.status === "failed";
227
+ },
228
+ enumerable: true,
229
+ configurable: true
230
+ };
231
+ }
232
+ if (Object.keys(computedProperties).length > 0) {
233
+ Object.defineProperties(jobResult, computedProperties);
234
+ }
235
+ return jobResult;
236
+ }
237
+ function enrichParseResult(parseResult2, scope) {
238
+ if (scope.namespace !== void 0) {
239
+ parseResult2.namespace = scope.namespace;
240
+ }
241
+ if (scope.documentId !== void 0) {
242
+ parseResult2.documentId = scope.documentId;
243
+ }
244
+ return parseResult2;
245
+ }
246
+ function sanitizePath(path4) {
247
+ let sanitized = path4.replace(/^\/+/, "");
248
+ sanitized = sanitized.replace(/\.\.(\/|\\)/g, "");
249
+ sanitized = sanitized.replace(/\\/g, "/");
250
+ return sanitized;
251
+ }
252
+ function getFileExtension(filename) {
253
+ const match = filename.match(/\.([^.]+)$/);
254
+ return match ? match[1].toLowerCase() : "";
255
+ }
256
+ function jitter(value, percent = 0.2) {
257
+ const randomFactor = 1 + (Math.random() * 2 - 1) * percent;
258
+ return Math.round(value * randomFactor);
259
+ }
260
+ var init_utils = __esm({
261
+ "src/lib/utils.ts"() {
262
+ "use strict";
263
+ init_constants();
264
+ }
265
+ });
266
+
267
+ // src/storage/parsed-document-storage.ts
268
+ async function syncParseResultToParsedDocumentStorage(params) {
269
+ const documentId = params.documentId ?? params.result.documentId ?? params.result.jobId;
270
+ const revisionKey = params.revisionKey ?? params.result.jobId;
271
+ for await (const object of collectResultLayoutObjects(params.result)) {
272
+ params.signal?.throwIfAborted();
273
+ await params.storage.writeObject({
274
+ documentId,
275
+ revisionKey,
276
+ ...object
277
+ });
278
+ }
279
+ await params.beforeCommit?.();
280
+ params.signal?.throwIfAborted();
281
+ const commit = {
282
+ version: 1,
283
+ documentId,
284
+ revisionKey,
285
+ source: params.source ?? "resultZip",
286
+ committedAt: (/* @__PURE__ */ new Date()).toISOString()
287
+ };
288
+ await writeParsedDocumentJsonObject(params.storage, {
289
+ documentId,
290
+ revisionKey,
291
+ path: parsedDocumentCommitPath,
292
+ value: commit
293
+ });
294
+ return {
295
+ result: params.result,
296
+ documentId,
297
+ revisionKey,
298
+ commit
299
+ };
300
+ }
301
+ async function readParsedDocumentCommit(params) {
302
+ const object = await params.storage.readObject({
303
+ documentId: params.documentId,
304
+ revisionKey: params.revisionKey,
305
+ path: parsedDocumentCommitPath
306
+ });
307
+ if (!object) {
308
+ return null;
309
+ }
310
+ const commit = readJsonObject(object);
311
+ return commit.revisionKey === params.revisionKey && commit.documentId === params.documentId ? commit : null;
312
+ }
313
+ function readJsonObject(object) {
314
+ return JSON.parse(Buffer.from(object.body).toString("utf8"));
315
+ }
316
+ async function* collectResultLayoutObjects(result) {
317
+ const storedPaths = /* @__PURE__ */ new Set();
318
+ let zip;
319
+ try {
320
+ zip = await import_jszip3.default.loadAsync(result.rawZip);
321
+ } catch {
322
+ }
323
+ if (zip) {
324
+ for (const entry of Object.values(zip.files)) {
325
+ if (entry.dir || entry.name === "result.zip") continue;
326
+ const path4 = normalizeResultPath(entry.name);
327
+ if (isSdkMetadataPath(path4)) continue;
328
+ storedPaths.add(path4);
329
+ yield {
330
+ path: path4,
331
+ body: await entry.async("nodebuffer"),
332
+ contentType: inferContentTypeFromPath(path4)
333
+ };
334
+ }
335
+ }
336
+ for (const object of generateMissingResultObjects(result, storedPaths)) yield object;
337
+ }
338
+ function* generateMissingResultObjects(result, storedPaths) {
339
+ if (!storedPaths.has("manifest.json")) yield createJsonObject("manifest.json", result.manifest);
340
+ if (!storedPaths.has("chunks.json"))
341
+ yield createJsonObject("chunks.json", keysToSnake(serializeChunks2(result.chunks)));
342
+ if (!storedPaths.has("doc_nav.json") && result.docNav)
343
+ yield createJsonObject("doc_nav.json", result.docNav);
344
+ if (!storedPaths.has("full.md") && result.fullMarkdown)
345
+ yield createTextObject("full.md", result.fullMarkdown, markdownContentType);
346
+ if (!storedPaths.has("chunks_slim.json") && result.chunksSlim)
347
+ yield createJsonObject("chunks_slim.json", { chunks: result.chunksSlim });
348
+ if (!storedPaths.has("hierarchy.json") && result.hierarchy)
349
+ yield createJsonObject("hierarchy.json", result.hierarchy);
350
+ if (!storedPaths.has("toc_hierarchies.json") && result.tocHierarchies)
351
+ yield createJsonObject("toc_hierarchies.json", result.tocHierarchies);
352
+ if (!storedPaths.has("kb.csv") && result.kbCsv)
353
+ yield createTextObject("kb.csv", result.kbCsv, csvContentType);
354
+ if (!storedPaths.has("hierarchy_view.html") && result.hierarchyViewHtml)
355
+ yield createTextObject("hierarchy_view.html", result.hierarchyViewHtml, htmlContentType);
356
+ for (const chunk of result.imageChunks) {
357
+ const path4 = normalizeResultPath(chunk.filePath);
358
+ if (storedPaths.has(path4)) continue;
359
+ yield {
360
+ path: path4,
361
+ body: chunk.data,
362
+ contentType: inferContentTypeFromPath(path4),
363
+ metadata: { chunkId: chunk.chunkId, chunkType: chunk.type, sourcePath: chunk.filePath }
364
+ };
365
+ }
366
+ for (const chunk of result.tableChunks) {
367
+ const path4 = normalizeResultPath(chunk.filePath);
368
+ if (storedPaths.has(path4)) continue;
369
+ yield {
370
+ path: path4,
371
+ body: Buffer.from(chunk.html, "utf8"),
372
+ contentType: htmlContentType,
373
+ metadata: { chunkId: chunk.chunkId, chunkType: chunk.type, sourcePath: chunk.filePath }
374
+ };
375
+ }
376
+ }
377
+ function serializeChunks2(chunks) {
378
+ return {
379
+ chunks: chunks.map((chunk) => {
380
+ const rawChunk = {
381
+ chunkId: chunk.chunkId,
382
+ type: chunk.type,
383
+ contentSource: chunk.contentSource,
384
+ content: chunk.content,
385
+ path: chunk.path,
386
+ metadata: chunk.metadata
387
+ };
388
+ if (chunk.type === "image" || chunk.type === "table") {
389
+ return {
390
+ ...rawChunk,
391
+ filePath: chunk.filePath,
392
+ assetUrl: chunk.assetUrl
393
+ };
394
+ }
395
+ return rawChunk;
396
+ })
397
+ };
398
+ }
399
+ async function writeParsedDocumentJsonObject(storage, params) {
400
+ await storage.writeObject({
401
+ documentId: params.documentId,
402
+ revisionKey: params.revisionKey,
403
+ path: params.path,
404
+ body: Buffer.from(JSON.stringify(params.value, null, 2), "utf8"),
405
+ contentType: jsonContentType
406
+ });
407
+ }
408
+ function createJsonObject(path4, value) {
409
+ return {
410
+ path: path4,
411
+ body: Buffer.from(JSON.stringify(value, null, 2), "utf8"),
412
+ contentType: jsonContentType
413
+ };
414
+ }
415
+ function createTextObject(path4, value, contentType) {
416
+ return {
417
+ path: path4,
418
+ body: Buffer.from(value, "utf8"),
419
+ contentType
420
+ };
421
+ }
422
+ function inferContentTypeFromPath(filePath) {
423
+ const lower = filePath.toLowerCase();
424
+ if (lower.endsWith(".json")) return jsonContentType;
425
+ if (lower.endsWith(".md")) return markdownContentType;
426
+ if (lower.endsWith(".csv")) return csvContentType;
427
+ if (lower.endsWith(".html") || lower.endsWith(".htm")) return htmlContentType;
428
+ if (lower.endsWith(".png")) return "image/png";
429
+ if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
430
+ if (lower.endsWith(".gif")) return "image/gif";
431
+ if (lower.endsWith(".webp")) return "image/webp";
432
+ if (lower.endsWith(".svg")) return "image/svg+xml";
433
+ return binaryContentType;
434
+ }
435
+ function normalizeResultPath(value) {
436
+ const normalized = value.replaceAll("\\", "/").replace(/^\.\/+/, "");
437
+ if (normalized.length === 0 || normalized.includes("\0") || normalized.startsWith("/") || normalized.split("/").some((part) => part.length === 0 || part === "." || part === "..")) {
438
+ throw new Error(`Invalid parsed result path: ${value}`);
439
+ }
440
+ return normalized;
441
+ }
442
+ function isSdkMetadataPath(path4) {
443
+ return path4 === ".knowhere-sdk" || path4.startsWith(".knowhere-sdk/");
444
+ }
445
+ var import_jszip3, parsedDocumentCommitPath, jsonContentType, markdownContentType, csvContentType, htmlContentType, binaryContentType;
446
+ var init_parsed_document_storage = __esm({
447
+ "src/storage/parsed-document-storage.ts"() {
448
+ "use strict";
449
+ import_jszip3 = __toESM(require("jszip"));
450
+ init_utils();
451
+ parsedDocumentCommitPath = ".knowhere-sdk/commit.json";
452
+ jsonContentType = "application/json; charset=utf-8";
453
+ markdownContentType = "text/markdown; charset=utf-8";
454
+ csvContentType = "text/csv; charset=utf-8";
455
+ htmlContentType = "text/html; charset=utf-8";
456
+ binaryContentType = "application/octet-stream";
457
+ }
458
+ });
459
+
460
+ // src/storage/sequential-parsed-document-archive.ts
461
+ var sequential_parsed_document_archive_exports = {};
462
+ __export(sequential_parsed_document_archive_exports, {
463
+ openSequentialArchive: () => openSequentialArchive
464
+ });
465
+ async function openSequentialArchive(params, identity) {
466
+ const response = await fetch(params.resultUrl, { signal: params.signal });
467
+ if (!response.ok || !response.body)
468
+ throw new Error(`Parsed result download failed (HTTP ${response.status}).`);
469
+ if (createDownloadIdentity(params.jobId, response) !== identity) {
470
+ await response.body.cancel();
471
+ throw new Error("Parsed result archive identity changed before sequential download.");
472
+ }
473
+ let directory;
474
+ try {
475
+ directory = await (0, import_promises2.mkdtemp)((0, import_path2.join)((0, import_os.tmpdir)(), "knowhere-sync-"));
476
+ } catch (error) {
477
+ await response.body.cancel();
478
+ throw new ParsedDocumentArchiveError(error);
479
+ }
480
+ let file;
481
+ try {
482
+ file = await (0, import_promises2.open)((0, import_path2.join)(directory, "archive.zip"), "w+");
483
+ } catch (error) {
484
+ await response.body.cancel();
485
+ await (0, import_promises2.rm)(directory, { recursive: true, force: true });
486
+ throw new ParsedDocumentArchiveError(error);
487
+ }
488
+ const input = import_stream.Readable.fromWeb(response.body);
489
+ const parser = (0, import_parse.default)({ forceStream: true });
490
+ const checksums = /* @__PURE__ */ new Map();
491
+ let archiveSize = 0;
492
+ const spool = new import_stream.Transform({
493
+ transform(chunk, _encoding, callback) {
494
+ archiveSize += chunk.length;
495
+ void file.writeFile(chunk).then(
496
+ () => {
497
+ callback(null, chunk);
498
+ },
499
+ (error) => {
500
+ callback(new ParsedDocumentArchiveError(error));
501
+ }
502
+ );
503
+ }
504
+ });
505
+ const completed = (0, import_promises.pipeline)(input, spool, parser, { signal: params.signal });
506
+ void completed.catch(() => void 0);
507
+ return {
508
+ identity,
509
+ downloadIdentity: identity,
510
+ canSkipEntries: false,
511
+ entries: (async function* () {
512
+ for await (const value of parser) {
513
+ const entry = value;
514
+ if (entry.type === "Directory" || !isSnapshotPath(entry.path)) {
515
+ await entry.autodrain().promise();
516
+ continue;
517
+ }
518
+ const path4 = normalizeResultPath(entry.path);
519
+ yield {
520
+ path: path4,
521
+ read: () => {
522
+ let checksum = 0;
523
+ let size = 0;
524
+ return import_stream.Readable.toWeb(entry).pipeThrough(
525
+ new TransformStream({
526
+ transform(chunk, controller) {
527
+ checksum = (0, import_zlib.crc32)(chunk, checksum);
528
+ size += chunk.length;
529
+ controller.enqueue(chunk);
530
+ },
531
+ flush() {
532
+ checksums.set(path4, { checksum, size });
533
+ }
534
+ })
535
+ );
536
+ }
537
+ };
538
+ }
539
+ await completed;
540
+ const reader = new import_zip.ZipReader(new DiskArchiveReader(file, archiveSize), {
541
+ useWebWorkers: false
542
+ });
543
+ try {
544
+ const files = (await reader.getEntries()).filter(
545
+ (entry) => !entry.directory && isSnapshotPath(entry.filename)
546
+ );
547
+ if (files.length !== checksums.size)
548
+ throw new Error("Parsed result ZIP directory does not match sequential entries.");
549
+ for (const entry of files) {
550
+ const actual = checksums.get(normalizeResultPath(entry.filename));
551
+ if (!actual || actual.checksum !== entry.signature || actual.size !== entry.uncompressedSize)
552
+ throw new Error(`Invalid parsed result ZIP checksum or size: ${entry.filename}`);
553
+ }
554
+ } finally {
555
+ await reader.close();
556
+ }
557
+ })(),
558
+ close: async () => {
559
+ input.destroy();
560
+ spool.destroy();
561
+ parser.destroy();
562
+ await completed.catch(() => void 0);
563
+ try {
564
+ await file.close();
565
+ } finally {
566
+ await (0, import_promises2.rm)(directory, { recursive: true, force: true });
567
+ }
568
+ }
569
+ };
570
+ }
571
+ var import_stream, import_promises, import_promises2, import_os, import_path2, import_zlib, import_zip, import_parse, DiskArchiveReader;
572
+ var init_sequential_parsed_document_archive = __esm({
573
+ "src/storage/sequential-parsed-document-archive.ts"() {
574
+ "use strict";
575
+ import_stream = require("stream");
576
+ import_promises = require("stream/promises");
577
+ import_promises2 = require("fs/promises");
578
+ import_os = require("os");
579
+ import_path2 = require("path");
580
+ import_zlib = require("zlib");
581
+ import_zip = require("@zip.js/zip.js");
582
+ import_parse = __toESM(require("unzipper/lib/parse.js"));
583
+ init_parsed_document_archive_error();
584
+ init_parsed_document_storage();
585
+ init_streamed_parsed_document_sync();
586
+ DiskArchiveReader = class extends import_zip.Reader {
587
+ constructor(file, size) {
588
+ super(file);
589
+ this.file = file;
590
+ this.size = size;
591
+ }
592
+ file;
593
+ async readUint8Array(index, length) {
594
+ const body = new Uint8Array(length);
595
+ const { bytesRead } = await this.file.read(body, 0, length, index);
596
+ if (bytesRead !== length) throw new Error("Truncated parsed result ZIP.");
597
+ return body;
598
+ }
599
+ };
600
+ }
601
+ });
602
+
603
+ // src/storage/streamed-parsed-document-sync.ts
604
+ async function syncRemoteResultArchive(params) {
605
+ params.signal?.throwIfAborted();
606
+ const startedAt = Date.now();
607
+ const maxEntries = Math.max(1, params.limits?.maxEntriesPerSync ?? 100);
608
+ const deadlineMs = Math.max(1, params.limits?.archiveSyncDeadlineMs ?? 12e4);
609
+ const previous = await params.storage.readSyncProgress(params);
610
+ const source = await openArchive(params);
611
+ const entryHashes = [];
612
+ const paths = /* @__PURE__ */ new Set();
613
+ let entryIndex = 0;
614
+ let copiedEntries = 0;
615
+ const nextEntry = previous?.status === "completed" ? 0 : previous?.nextArchiveEntry ?? 0;
616
+ try {
617
+ if (previous?.archiveIdentity && previous.archiveIdentity !== source.identity) {
618
+ throw new Error("Parsed result archive identity changed; start a new revision.");
619
+ }
620
+ if (previous?.pinnedRevisionKey && previous.pinnedRevisionKey !== params.revisionKey) {
621
+ throw new Error("Parsed result revision changed while resuming archive sync.");
622
+ }
623
+ for await (const entry of source.entries) {
624
+ params.signal?.throwIfAborted();
625
+ if (paths.has(entry.path)) throw new Error(`Duplicate parsed result path: ${entry.path}`);
626
+ paths.add(entry.path);
627
+ if (entryIndex >= nextEntry && copiedEntries > 0 && (copiedEntries >= maxEntries || Date.now() - startedAt >= deadlineMs)) {
628
+ return { documentId: params.documentId, revisionKey: params.revisionKey, completed: false };
629
+ }
630
+ if (entryIndex < nextEntry && source.canSkipEntries) {
631
+ const previousHash = previous?.archiveEntryHashes?.[entryIndex];
632
+ if (!previousHash) throw new Error("Parsed archive checkpoint is missing an entry hash.");
633
+ entryHashes.push(previousHash);
634
+ entryIndex += 1;
635
+ continue;
636
+ }
637
+ const hash = (0, import_crypto.createHash)("sha256");
638
+ const body = entry.read().pipeThrough(
639
+ new TransformStream({
640
+ transform(chunk, controller) {
641
+ params.signal?.throwIfAborted();
642
+ hash.update(chunk);
643
+ controller.enqueue(chunk);
644
+ }
645
+ })
646
+ );
647
+ if (entryIndex < nextEntry) {
648
+ await body.pipeTo(new WritableStream(), { signal: params.signal });
649
+ } else {
650
+ await writeStream(params.storage, {
651
+ documentId: params.documentId,
652
+ revisionKey: params.revisionKey,
653
+ path: entry.path,
654
+ body,
655
+ contentType: inferContentTypeFromPath(entry.path),
656
+ signal: params.signal
657
+ });
658
+ }
659
+ const entryHash = (0, import_crypto.createHash)("sha256").update(entry.path).update(hash.digest()).digest("hex");
660
+ if (entryIndex < nextEntry && previous?.archiveEntryHashes?.[entryIndex] !== entryHash) {
661
+ throw new Error("Parsed result archive entry changed while resuming.");
662
+ }
663
+ entryHashes.push(entryHash);
664
+ entryIndex += 1;
665
+ if (entryIndex <= nextEntry) continue;
666
+ copiedEntries += 1;
667
+ await params.storage.writeSyncProgress({
668
+ documentId: params.documentId,
669
+ revisionKey: params.revisionKey,
670
+ pinnedRevisionKey: params.revisionKey,
671
+ archiveIdentity: source.identity,
672
+ archiveEntryHashes: [...entryHashes],
673
+ nextArchiveEntry: entryIndex,
674
+ status: "running",
675
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
676
+ });
677
+ }
678
+ await validateRequiredObjects(params, paths);
679
+ await params.verifyRevision?.();
680
+ params.signal?.throwIfAborted();
681
+ const commit = {
682
+ version: 1,
683
+ documentId: params.documentId,
684
+ revisionKey: params.revisionKey,
685
+ source: "resultZip",
686
+ committedAt: (/* @__PURE__ */ new Date()).toISOString()
687
+ };
688
+ await params.storage.writeObject({
689
+ documentId: params.documentId,
690
+ revisionKey: params.revisionKey,
691
+ path: parsedDocumentCommitPath,
692
+ body: Buffer.from(JSON.stringify(commit, null, 2)),
693
+ contentType: "application/json; charset=utf-8"
694
+ });
695
+ await params.storage.writeSyncProgress({
696
+ documentId: params.documentId,
697
+ revisionKey: params.revisionKey,
698
+ pinnedRevisionKey: params.revisionKey,
699
+ archiveIdentity: source.identity,
700
+ archiveEntryHashes: entryHashes,
701
+ nextArchiveEntry: entryIndex,
702
+ status: "completed",
703
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
704
+ });
705
+ if (params.retainResultArchive) {
706
+ try {
707
+ await retainArchive(params, source.downloadIdentity);
708
+ } catch {
709
+ }
710
+ }
711
+ return {
712
+ documentId: params.documentId,
713
+ revisionKey: params.revisionKey,
714
+ completed: true,
715
+ commit
716
+ };
717
+ } finally {
718
+ await source.close();
719
+ }
720
+ }
721
+ async function openArchive(params) {
722
+ const probe = await fetch(params.resultUrl, {
723
+ headers: { Range: "bytes=0-0" },
724
+ signal: params.signal
725
+ });
726
+ if (!probe.ok) {
727
+ await probe.body?.cancel();
728
+ throw new Error(`Parsed result download failed (HTTP ${probe.status}).`);
729
+ }
730
+ const identity = createDownloadIdentity(params.jobId, probe);
731
+ const supportsRange = probe.status === 206 && probe.headers.has("content-range");
732
+ await probe.body?.cancel();
733
+ if (!supportsRange) {
734
+ const { openSequentialArchive: openSequentialArchive2 } = await Promise.resolve().then(() => (init_sequential_parsed_document_archive(), sequential_parsed_document_archive_exports));
735
+ return openSequentialArchive2(params, identity);
736
+ }
737
+ const options = {
738
+ forceRangeRequests: true,
739
+ preventHeadRequest: true,
740
+ checkResourceChanges: true,
741
+ fetch: async (input, init) => {
742
+ const response = await fetch(input, { ...init, signal: params.signal });
743
+ if (response.status !== 206) {
744
+ await response.body?.cancel();
745
+ throw new Error(`Parsed result Range request failed (HTTP ${response.status}).`);
746
+ }
747
+ return response;
748
+ }
749
+ };
750
+ const reader = new import_zip2.ZipReader(new import_zip2.HttpRangeReader(params.resultUrl, options), {
751
+ useWebWorkers: false,
752
+ checkCrc32: true
753
+ });
754
+ try {
755
+ const entries = await reader.getEntries();
756
+ const files = entries.filter(
757
+ (entry) => !entry.directory && isSnapshotPath(entry.filename)
758
+ );
759
+ const archiveIdentity = (0, import_crypto.createHash)("sha256").update(identity).update(
760
+ JSON.stringify(
761
+ files.map((entry) => [entry.filename, entry.uncompressedSize, entry.signature])
762
+ )
763
+ ).digest("hex");
764
+ return {
765
+ identity: archiveIdentity,
766
+ downloadIdentity: identity,
767
+ canSkipEntries: true,
768
+ entries: (function* () {
769
+ for (const entry of files)
770
+ yield {
771
+ path: normalizeResultPath(entry.filename),
772
+ read: () => readRangeEntry(entry, params.signal)
773
+ };
774
+ })(),
775
+ close: async () => {
776
+ await reader.close();
777
+ }
778
+ };
779
+ } catch (error) {
780
+ await reader.close();
781
+ throw error;
782
+ }
783
+ }
784
+ function readRangeEntry(entry, signal) {
785
+ const stream = new TransformStream();
786
+ void entry.getData(stream.writable, { signal, checkCrc32: true, useWebWorkers: false }).catch(async (error) => {
787
+ try {
788
+ await stream.writable.abort(error);
789
+ } catch {
790
+ }
791
+ });
792
+ return stream.readable;
793
+ }
794
+ async function writeStream(storage, params) {
795
+ if (storage.writeObjectStream) {
796
+ try {
797
+ await storage.writeObjectStream(params);
798
+ } catch (error) {
799
+ await params.body.cancel(error).catch(() => void 0);
800
+ throw error;
801
+ }
802
+ return;
803
+ }
804
+ const parts = [];
805
+ for await (const part of params.body) {
806
+ params.signal?.throwIfAborted();
807
+ parts.push(part);
808
+ }
809
+ await storage.writeObject({ ...params, body: Buffer.concat(parts) });
810
+ }
811
+ async function validateRequiredObjects(params, paths) {
812
+ const required = /* @__PURE__ */ new Set(["manifest.json", "chunks.json"]);
813
+ for (const path4 of required)
814
+ if (!paths.has(path4)) throw new Error(`Required parsed result file missing: ${path4}`);
815
+ const manifestObject = await params.storage.readObject({ ...params, path: "manifest.json" });
816
+ const chunksObject = await params.storage.readObject({ ...params, path: "chunks.json" });
817
+ if (!manifestObject || !chunksObject)
818
+ throw new Error("Required parsed result manifest.json or chunks.json was not persisted.");
819
+ const manifest = JSON.parse(Buffer.from(manifestObject.body).toString("utf8"));
820
+ const payload = JSON.parse(Buffer.from(chunksObject.body).toString("utf8"));
821
+ if (!isRecord2(manifest)) throw new Error("Invalid parsed result manifest.json.");
822
+ const manifestJobId = manifest.job_id ?? manifest.jobId;
823
+ if (manifestJobId !== params.jobId)
824
+ throw new Error("Parsed result manifest job identity differs from the pinned job.");
825
+ const chunks = Array.isArray(payload) ? payload : isRecord2(payload) ? payload.chunks : void 0;
826
+ if (!Array.isArray(chunks)) throw new Error("Invalid parsed result chunks.json.");
827
+ if (isRecord2(manifest.files)) {
828
+ for (const path4 of Object.values(manifest.files))
829
+ if (typeof path4 === "string") required.add(normalizeResultPath(path4));
830
+ }
831
+ for (const chunk of chunks) {
832
+ if (!isRecord2(chunk)) throw new Error("Invalid parsed result chunk.");
833
+ const metadata = chunk.metadata;
834
+ const chunkType = chunk.type ?? chunk.chunk_type ?? chunk.chunkType;
835
+ const filePath = chunk.file_path ?? chunk.filePath ?? (isRecord2(metadata) ? metadata.file_path ?? metadata.filePath : void 0) ?? (chunkType === "image" || chunkType === "table" ? chunk.path : void 0);
836
+ if (typeof filePath === "string") required.add(normalizeResultPath(filePath));
837
+ if (!isRecord2(metadata)) continue;
838
+ const pageAssets = metadata.page_assets ?? metadata.pageAssets;
839
+ if (!Array.isArray(pageAssets)) continue;
840
+ for (const asset of pageAssets)
841
+ if (isRecord2(asset)) {
842
+ const ref = asset.artifact_ref ?? asset.artifactRef;
843
+ if (typeof ref === "string") required.add(normalizeResultPath(ref));
844
+ }
845
+ }
846
+ for (const path4 of required)
847
+ if (!paths.has(path4)) throw new Error(`Required parsed result file missing: ${path4}`);
848
+ }
849
+ async function retainArchive(params, expectedIdentity) {
850
+ const response = await fetch(params.resultUrl, { signal: params.signal });
851
+ if (!response.ok || !response.body)
852
+ throw new Error(`Optional archive download failed (HTTP ${response.status}).`);
853
+ if (createDownloadIdentity(params.jobId, response) !== expectedIdentity) {
854
+ await response.body.cancel();
855
+ throw new Error("Optional result archive identity changed.");
856
+ }
857
+ await writeStream(params.storage, {
858
+ documentId: params.documentId,
859
+ revisionKey: params.revisionKey,
860
+ path: "result.zip",
861
+ body: response.body,
862
+ contentType: "application/zip",
863
+ signal: params.signal
864
+ });
865
+ }
866
+ function isSnapshotPath(path4) {
867
+ const normalized = normalizeResultPath(path4.replace(/\/$/, ""));
868
+ return normalized !== "result.zip" && normalized !== ".knowhere-sdk" && !normalized.startsWith(".knowhere-sdk/");
869
+ }
870
+ function isRecord2(value) {
871
+ return typeof value === "object" && value !== null && !Array.isArray(value);
872
+ }
873
+ function createDownloadIdentity(jobId, response) {
874
+ return (0, import_crypto.createHash)("sha256").update(
875
+ JSON.stringify([
876
+ jobId,
877
+ response.headers.get("etag"),
878
+ response.headers.get("last-modified"),
879
+ response.headers.get("content-range")?.split("/").at(-1) ?? response.headers.get("content-length")
880
+ ])
881
+ ).digest("hex");
882
+ }
883
+ var import_crypto, import_zip2;
884
+ var init_streamed_parsed_document_sync = __esm({
885
+ "src/storage/streamed-parsed-document-sync.ts"() {
886
+ "use strict";
887
+ import_crypto = require("crypto");
888
+ import_zip2 = require("@zip.js/zip.js");
889
+ init_parsed_document_storage();
890
+ }
891
+ });
29
892
 
30
893
  // src/index.ts
31
894
  var index_exports = {};
@@ -49,6 +912,7 @@ __export(index_exports, {
49
912
  NODE_SDK_DOCUMENT_METADATA_DEFAULTS: () => NODE_SDK_DOCUMENT_METADATA_DEFAULTS,
50
913
  NetworkError: () => NetworkError,
51
914
  NotFoundError: () => NotFoundError,
915
+ ParsedDocumentArchiveError: () => ParsedDocumentArchiveError,
52
916
  PaymentRequiredError: () => PaymentRequiredError,
53
917
  PermissionDeniedError: () => PermissionDeniedError,
54
918
  PollingTimeoutError: () => PollingTimeoutError,
@@ -66,7 +930,7 @@ __export(index_exports, {
66
930
  module.exports = __toCommonJS(index_exports);
67
931
 
68
932
  // src/client.ts
69
- var import_path3 = __toESM(require("path"));
933
+ var import_path4 = __toESM(require("path"));
70
934
 
71
935
  // src/lib/http-client.ts
72
936
  var import_axios = __toESM(require("axios"));
@@ -74,80 +938,14 @@ var import_axios = __toESM(require("axios"));
74
938
  // src/version.ts
75
939
  var VERSION = "2.1.3";
76
940
 
77
- // src/constants.ts
78
- var DEFAULT_BASE_URL = "https://api.knowhereto.ai";
79
- var DEFAULT_TIMEOUT = 6e4;
80
- var DEFAULT_MAX_RETRIES = 5;
81
- var DEFAULT_POLL_INTERVAL = 1e4;
82
- var MAX_POLL_INTERVAL = 3e4;
83
- var DEFAULT_POLL_TIMEOUT = 18e5;
84
- var POLL_INTERVAL_INCREASE_THRESHOLD = 6e4;
85
- var POLL_INTERVAL_MULTIPLIER = 1.2;
86
- var INITIAL_RETRY_DELAY = 500;
87
- var MAX_RETRY_DELAY = 3e4;
88
- var RETRY_DELAY_BASE = 2;
89
- var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([409, 429, 502, 503, 504]);
90
- var TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["done", "failed"]);
91
- var ENV = {
92
- API_KEY: "KNOWHERE_API_KEY",
93
- BASE_URL: "KNOWHERE_BASE_URL",
94
- LOG_LEVEL: "KNOWHERE_LOG_LEVEL"
95
- };
941
+ // src/lib/http-client.ts
942
+ init_constants();
96
943
 
97
- // src/errors/base.ts
98
- var KnowhereError = class extends Error {
99
- constructor(message) {
100
- super(message);
101
- this.name = "KnowhereError";
102
- Object.setPrototypeOf(this, new.target.prototype);
103
- }
104
- };
105
- var NetworkError = class extends KnowhereError {
106
- constructor(message, cause) {
107
- super(message);
108
- this.cause = cause;
109
- this.name = "NetworkError";
110
- }
111
- cause;
112
- };
113
- var TimeoutError = class extends NetworkError {
114
- constructor(message = "Request timed out") {
115
- super(message);
116
- this.name = "TimeoutError";
117
- }
118
- };
119
- var PollingTimeoutError = class extends KnowhereError {
120
- constructor(message = "Polling timed out", elapsedMs) {
121
- super(message);
122
- this.elapsedMs = elapsedMs;
123
- this.name = "PollingTimeoutError";
124
- }
125
- elapsedMs;
126
- };
127
- var ChecksumError = class extends KnowhereError {
128
- constructor(message = "Checksum verification failed", expected, actual) {
129
- super(message);
130
- this.expected = expected;
131
- this.actual = actual;
132
- this.name = "ChecksumError";
133
- }
134
- expected;
135
- actual;
136
- };
137
- var ValidationError = class extends KnowhereError {
138
- constructor(message) {
139
- super(message);
140
- this.name = "ValidationError";
141
- }
142
- };
143
- var InvalidStateError = class extends KnowhereError {
144
- constructor(message) {
145
- super(message);
146
- this.name = "InvalidStateError";
147
- }
148
- };
944
+ // src/errors/index.ts
945
+ init_base();
149
946
 
150
947
  // src/errors/api-errors.ts
948
+ init_base();
151
949
  var APIError = class extends KnowhereError {
152
950
  constructor(message, statusCode, code, requestId, details, body) {
153
951
  super(message);
@@ -234,162 +1032,48 @@ function createAPIError(statusCode, message, code, requestId, details, body, ret
234
1032
  return new AuthenticationError(message, code, requestId, details, body);
235
1033
  case 402:
236
1034
  return new PaymentRequiredError(message, code, requestId, details, body);
237
- case 403:
238
- return new PermissionDeniedError(message, code, requestId, details, body);
239
- case 404:
240
- return new NotFoundError(message, code, requestId, details, body);
241
- case 409:
242
- return new ConflictError(message, code, requestId, details, body);
243
- case 429:
244
- return new RateLimitError(message, code, requestId, details, body, retryAfter);
245
- case 500:
246
- return new InternalServerError(message, code, requestId, details, body);
247
- case 502:
248
- case 503:
249
- return new ServiceUnavailableError(message, statusCode, code, requestId, details, body);
250
- case 504:
251
- return new GatewayTimeoutError(message, code, requestId, details, body);
252
- default:
253
- return new APIError(message, statusCode, code, requestId, details, body);
254
- }
255
- }
256
-
257
- // src/errors/job-errors.ts
258
- var JobFailedError = class extends KnowhereError {
259
- constructor(message, code, jobResult) {
260
- super(message);
261
- this.code = code;
262
- this.jobResult = jobResult;
263
- this.name = "JobFailedError";
264
- }
265
- code;
266
- jobResult;
267
- };
268
-
269
- // src/lib/utils.ts
270
- function sleep(ms) {
271
- return new Promise((resolve2) => setTimeout(resolve2, ms));
272
- }
273
- function snakeToCamel(str) {
274
- return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
275
- }
276
- function camelToSnake(str) {
277
- return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
278
- }
279
- function keysToCamel(obj) {
280
- if (obj === null || obj === void 0) {
281
- return obj;
282
- }
283
- if (Array.isArray(obj)) {
284
- return obj.map((item) => keysToCamel(item));
285
- }
286
- if (typeof obj === "object" && obj.constructor === Object) {
287
- const result = {};
288
- for (const [key, value] of Object.entries(obj)) {
289
- result[snakeToCamel(key)] = keysToCamel(value);
290
- }
291
- return result;
292
- }
293
- return obj;
294
- }
295
- function keysToSnake(obj) {
296
- if (obj === null || obj === void 0) {
297
- return obj;
298
- }
299
- if (Array.isArray(obj)) {
300
- return obj.map((item) => keysToSnake(item));
301
- }
302
- if (typeof obj === "object" && obj.constructor === Object) {
303
- const result = {};
304
- for (const [key, value] of Object.entries(obj)) {
305
- result[camelToSnake(key)] = keysToSnake(value);
306
- }
307
- return result;
308
- }
309
- return obj;
310
- }
311
- function parseDates(obj) {
312
- if (obj === null || obj === void 0) {
313
- return obj;
314
- }
315
- if (Array.isArray(obj)) {
316
- return obj.map((item) => parseDates(item));
317
- }
318
- if (typeof obj === "object" && obj.constructor === Object) {
319
- const result = {};
320
- for (const [key, value] of Object.entries(obj)) {
321
- if ((key.endsWith("At") || key.endsWith("Date")) && typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
322
- result[key] = new Date(value);
323
- } else {
324
- result[key] = parseDates(value);
325
- }
326
- }
327
- return result;
328
- }
329
- return obj;
330
- }
331
- function isTerminalStatus(status) {
332
- return TERMINAL_JOB_STATUSES.has(status);
333
- }
334
- function enrichJobResult(jobResult) {
335
- const computedProperties = {};
336
- if (!Object.getOwnPropertyDescriptor(jobResult, "isTerminal")) {
337
- computedProperties.isTerminal = {
338
- get() {
339
- return isTerminalStatus(this.status);
340
- },
341
- enumerable: true,
342
- configurable: true
343
- };
344
- }
345
- if (!Object.getOwnPropertyDescriptor(jobResult, "isDone")) {
346
- computedProperties.isDone = {
347
- get() {
348
- return this.status === "done";
349
- },
350
- enumerable: true,
351
- configurable: true
352
- };
353
- }
354
- if (!Object.getOwnPropertyDescriptor(jobResult, "isFailed")) {
355
- computedProperties.isFailed = {
356
- get() {
357
- return this.status === "failed";
358
- },
359
- enumerable: true,
360
- configurable: true
361
- };
362
- }
363
- if (Object.keys(computedProperties).length > 0) {
364
- Object.defineProperties(jobResult, computedProperties);
1035
+ case 403:
1036
+ return new PermissionDeniedError(message, code, requestId, details, body);
1037
+ case 404:
1038
+ return new NotFoundError(message, code, requestId, details, body);
1039
+ case 409:
1040
+ return new ConflictError(message, code, requestId, details, body);
1041
+ case 429:
1042
+ return new RateLimitError(message, code, requestId, details, body, retryAfter);
1043
+ case 500:
1044
+ return new InternalServerError(message, code, requestId, details, body);
1045
+ case 502:
1046
+ case 503:
1047
+ return new ServiceUnavailableError(message, statusCode, code, requestId, details, body);
1048
+ case 504:
1049
+ return new GatewayTimeoutError(message, code, requestId, details, body);
1050
+ default:
1051
+ return new APIError(message, statusCode, code, requestId, details, body);
365
1052
  }
366
- return jobResult;
367
1053
  }
368
- function enrichParseResult(parseResult2, scope) {
369
- if (scope.namespace !== void 0) {
370
- parseResult2.namespace = scope.namespace;
371
- }
372
- if (scope.documentId !== void 0) {
373
- parseResult2.documentId = scope.documentId;
1054
+
1055
+ // src/errors/job-errors.ts
1056
+ init_base();
1057
+ var JobFailedError = class extends KnowhereError {
1058
+ constructor(message, code, jobResult) {
1059
+ super(message);
1060
+ this.code = code;
1061
+ this.jobResult = jobResult;
1062
+ this.name = "JobFailedError";
374
1063
  }
375
- return parseResult2;
376
- }
377
- function sanitizePath(path4) {
378
- let sanitized = path4.replace(/^\/+/, "");
379
- sanitized = sanitized.replace(/\.\.(\/|\\)/g, "");
380
- sanitized = sanitized.replace(/\\/g, "/");
381
- return sanitized;
382
- }
383
- function getFileExtension(filename) {
384
- const match = filename.match(/\.([^.]+)$/);
385
- return match ? match[1].toLowerCase() : "";
386
- }
387
- function jitter(value, percent = 0.2) {
388
- const randomFactor = 1 + (Math.random() * 2 - 1) * percent;
389
- return Math.round(value * randomFactor);
390
- }
1064
+ code;
1065
+ jobResult;
1066
+ };
1067
+
1068
+ // src/errors/index.ts
1069
+ init_parsed_document_archive_error();
1070
+
1071
+ // src/lib/http-client.ts
1072
+ init_utils();
391
1073
 
392
1074
  // src/lib/retry.ts
1075
+ init_constants();
1076
+ init_utils();
393
1077
  function getErrorCode(error) {
394
1078
  const errorWithResponse = error;
395
1079
  return errorWithResponse?.response?.data?.error?.code ?? errorWithResponse?.response?.data?.code ?? errorWithResponse?.code;
@@ -815,6 +1499,8 @@ function isReadStream(obj) {
815
1499
  }
816
1500
 
817
1501
  // src/lib/polling.ts
1502
+ init_constants();
1503
+ init_utils();
818
1504
  async function pollJobStatus(httpClient, jobId, options) {
819
1505
  const pollInterval = options?.pollInterval ?? DEFAULT_POLL_INTERVAL;
820
1506
  const pollTimeout = options?.pollTimeout ?? DEFAULT_POLL_TIMEOUT;
@@ -865,6 +1551,7 @@ async function pollJobStatus(httpClient, jobId, options) {
865
1551
  var import_jszip = __toESM(require("jszip"));
866
1552
  var import_fs2 = require("fs");
867
1553
  var import_path = require("path");
1554
+ init_utils();
868
1555
  async function parseResult(httpClient, resultUrl, options) {
869
1556
  const zipBuffer = await httpClient.download(resultUrl);
870
1557
  if (options?.verifyChecksum !== false) {
@@ -1367,6 +2054,7 @@ function isMissingFileError(error) {
1367
2054
  }
1368
2055
 
1369
2056
  // src/resources/jobs.ts
2057
+ init_utils();
1370
2058
  var Jobs = class extends BaseResource {
1371
2059
  pendingUploadJobs = /* @__PURE__ */ new Map();
1372
2060
  /**
@@ -1607,7 +2295,7 @@ var Documents = class extends BaseResource {
1607
2295
  };
1608
2296
 
1609
2297
  // src/knowledge/knowledge.ts
1610
- var import_crypto2 = require("crypto");
2298
+ var import_crypto3 = require("crypto");
1611
2299
 
1612
2300
  // src/storage/asset-storage.ts
1613
2301
  var import_jszip2 = __toESM(require("jszip"));
@@ -1871,220 +2559,16 @@ function isRecord(value) {
1871
2559
  return typeof value === "object" && value !== null;
1872
2560
  }
1873
2561
 
1874
- // src/storage/parsed-document-storage.ts
1875
- var import_jszip3 = __toESM(require("jszip"));
1876
- var parsedDocumentCommitPath = ".knowhere-sdk/commit.json";
1877
- var jsonContentType = "application/json; charset=utf-8";
1878
- var markdownContentType = "text/markdown; charset=utf-8";
1879
- var csvContentType = "text/csv; charset=utf-8";
1880
- var htmlContentType = "text/html; charset=utf-8";
1881
- var binaryContentType = "application/octet-stream";
1882
- async function syncParseResultToParsedDocumentStorage(params) {
1883
- const documentId = params.documentId ?? params.result.documentId ?? params.result.jobId;
1884
- const revisionKey = params.revisionKey ?? params.result.jobId;
1885
- const objects = await collectResultLayoutObjects(params.result);
1886
- for (const object of objects) {
1887
- await params.storage.writeObject({
1888
- documentId,
1889
- revisionKey,
1890
- ...object
1891
- });
1892
- }
1893
- const commit = {
1894
- version: 1,
1895
- documentId,
1896
- revisionKey,
1897
- source: params.source ?? "resultZip",
1898
- committedAt: (/* @__PURE__ */ new Date()).toISOString()
1899
- };
1900
- await writeParsedDocumentJsonObject(params.storage, {
1901
- documentId,
1902
- revisionKey,
1903
- path: parsedDocumentCommitPath,
1904
- value: commit
1905
- });
1906
- return {
1907
- result: params.result,
1908
- documentId,
1909
- revisionKey,
1910
- commit
1911
- };
1912
- }
1913
- async function readParsedDocumentCommit(params) {
1914
- const object = await params.storage.readObject({
1915
- documentId: params.documentId,
1916
- revisionKey: params.revisionKey,
1917
- path: parsedDocumentCommitPath
1918
- });
1919
- if (!object) {
1920
- return null;
1921
- }
1922
- const commit = readJsonObject(object);
1923
- return commit.revisionKey === params.revisionKey && commit.documentId === params.documentId ? commit : null;
1924
- }
1925
- function readJsonObject(object) {
1926
- return JSON.parse(Buffer.from(object.body).toString("utf8"));
1927
- }
1928
- async function collectResultLayoutObjects(result) {
1929
- const objectsByPath = /* @__PURE__ */ new Map();
1930
- const rawZipObjects = await collectRawZipObjects(result.rawZip);
1931
- for (const object of rawZipObjects) {
1932
- objectsByPath.set(object.path, object);
1933
- }
1934
- for (const object of collectGeneratedResultObjects(result)) {
1935
- if (!objectsByPath.has(object.path)) {
1936
- objectsByPath.set(object.path, object);
1937
- }
1938
- }
1939
- return [...objectsByPath.values()].filter((object) => !isSdkMetadataPath(object.path));
1940
- }
1941
- async function collectRawZipObjects(rawZip) {
1942
- try {
1943
- const zip = await import_jszip3.default.loadAsync(rawZip);
1944
- const objects = [];
1945
- for (const entry of Object.values(zip.files)) {
1946
- if (entry.dir || entry.name === "result.zip") {
1947
- continue;
1948
- }
1949
- objects.push({
1950
- path: normalizeResultPath(entry.name),
1951
- body: new Uint8Array(await entry.async("nodebuffer")),
1952
- contentType: inferContentTypeFromPath(entry.name)
1953
- });
1954
- }
1955
- return objects;
1956
- } catch {
1957
- return [];
1958
- }
1959
- }
1960
- function collectGeneratedResultObjects(result) {
1961
- const objects = [
1962
- createJsonObject("manifest.json", result.manifest),
1963
- createJsonObject("chunks.json", keysToSnake(serializeChunks2(result.chunks)))
1964
- ];
1965
- if (result.docNav) {
1966
- objects.push(createJsonObject("doc_nav.json", result.docNav));
1967
- }
1968
- if (result.fullMarkdown) {
1969
- objects.push(createTextObject("full.md", result.fullMarkdown, markdownContentType));
1970
- }
1971
- if (result.chunksSlim) {
1972
- objects.push(createJsonObject("chunks_slim.json", { chunks: result.chunksSlim }));
1973
- }
1974
- if (result.hierarchy) {
1975
- objects.push(createJsonObject("hierarchy.json", result.hierarchy));
1976
- }
1977
- if (result.tocHierarchies) {
1978
- objects.push(createJsonObject("toc_hierarchies.json", result.tocHierarchies));
1979
- }
1980
- if (result.kbCsv) {
1981
- objects.push(createTextObject("kb.csv", result.kbCsv, csvContentType));
1982
- }
1983
- if (result.hierarchyViewHtml) {
1984
- objects.push(
1985
- createTextObject("hierarchy_view.html", result.hierarchyViewHtml, htmlContentType)
1986
- );
1987
- }
1988
- for (const chunk of result.imageChunks) {
1989
- objects.push({
1990
- path: normalizeResultPath(chunk.filePath),
1991
- body: new Uint8Array(chunk.data),
1992
- contentType: inferContentTypeFromPath(chunk.filePath),
1993
- metadata: {
1994
- chunkId: chunk.chunkId,
1995
- chunkType: chunk.type,
1996
- sourcePath: chunk.filePath
1997
- }
1998
- });
1999
- }
2000
- for (const chunk of result.tableChunks) {
2001
- objects.push({
2002
- path: normalizeResultPath(chunk.filePath),
2003
- body: Buffer.from(chunk.html, "utf8"),
2004
- contentType: htmlContentType,
2005
- metadata: {
2006
- chunkId: chunk.chunkId,
2007
- chunkType: chunk.type,
2008
- sourcePath: chunk.filePath
2009
- }
2010
- });
2011
- }
2012
- return objects;
2013
- }
2014
- function serializeChunks2(chunks) {
2015
- return {
2016
- chunks: chunks.map((chunk) => {
2017
- const rawChunk = {
2018
- chunkId: chunk.chunkId,
2019
- type: chunk.type,
2020
- contentSource: chunk.contentSource,
2021
- content: chunk.content,
2022
- path: chunk.path,
2023
- metadata: chunk.metadata
2024
- };
2025
- if (chunk.type === "image" || chunk.type === "table") {
2026
- return {
2027
- ...rawChunk,
2028
- filePath: chunk.filePath,
2029
- assetUrl: chunk.assetUrl
2030
- };
2031
- }
2032
- return rawChunk;
2033
- })
2034
- };
2035
- }
2036
- async function writeParsedDocumentJsonObject(storage, params) {
2037
- await storage.writeObject({
2038
- documentId: params.documentId,
2039
- revisionKey: params.revisionKey,
2040
- path: params.path,
2041
- body: Buffer.from(JSON.stringify(params.value, null, 2), "utf8"),
2042
- contentType: jsonContentType
2043
- });
2044
- }
2045
- function createJsonObject(path4, value) {
2046
- return {
2047
- path: path4,
2048
- body: Buffer.from(JSON.stringify(value, null, 2), "utf8"),
2049
- contentType: jsonContentType
2050
- };
2051
- }
2052
- function createTextObject(path4, value, contentType) {
2053
- return {
2054
- path: path4,
2055
- body: Buffer.from(value, "utf8"),
2056
- contentType
2057
- };
2058
- }
2059
- function inferContentTypeFromPath(filePath) {
2060
- const lower = filePath.toLowerCase();
2061
- if (lower.endsWith(".json")) return jsonContentType;
2062
- if (lower.endsWith(".md")) return markdownContentType;
2063
- if (lower.endsWith(".csv")) return csvContentType;
2064
- if (lower.endsWith(".html") || lower.endsWith(".htm")) return htmlContentType;
2065
- if (lower.endsWith(".png")) return "image/png";
2066
- if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
2067
- if (lower.endsWith(".gif")) return "image/gif";
2068
- if (lower.endsWith(".webp")) return "image/webp";
2069
- if (lower.endsWith(".svg")) return "image/svg+xml";
2070
- return binaryContentType;
2071
- }
2072
- function normalizeResultPath(value) {
2073
- const normalized = value.replaceAll("\\", "/").replace(/^\.\/+/, "");
2074
- if (normalized.length === 0 || normalized.includes("\0") || normalized.startsWith("/") || normalized.split("/").some((part) => part.length === 0 || part === "." || part === "..")) {
2075
- throw new Error(`Invalid parsed result path: ${value}`);
2076
- }
2077
- return normalized;
2078
- }
2079
- function isSdkMetadataPath(path4) {
2080
- return path4 === ".knowhere-sdk" || path4.startsWith(".knowhere-sdk/");
2081
- }
2562
+ // src/knowledge/knowledge.ts
2563
+ init_parsed_document_storage();
2564
+ init_utils();
2565
+ init_streamed_parsed_document_sync();
2082
2566
 
2083
2567
  // src/knowledge/local-store.ts
2084
- var import_crypto = require("crypto");
2085
- var import_os = __toESM(require("os"));
2568
+ var import_crypto2 = require("crypto");
2569
+ var import_os2 = __toESM(require("os"));
2086
2570
  var import_fs3 = require("fs");
2087
- var import_path2 = __toESM(require("path"));
2571
+ var import_path3 = __toESM(require("path"));
2088
2572
  var STORE_VERSION = 1;
2089
2573
  var LOCAL_DOCUMENT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
2090
2574
  var LocalKnowledgeStore = class {
@@ -2092,8 +2576,8 @@ var LocalKnowledgeStore = class {
2092
2576
  indexPath;
2093
2577
  resultCache = /* @__PURE__ */ new Map();
2094
2578
  constructor(cacheDirectory) {
2095
- this.cacheDirectory = cacheDirectory ?? import_path2.default.join(import_os.default.homedir(), ".knowhere-node-sdk", "knowledge");
2096
- this.indexPath = import_path2.default.join(this.cacheDirectory, "index.json");
2579
+ this.cacheDirectory = cacheDirectory ?? import_path3.default.join(import_os2.default.homedir(), ".knowhere-node-sdk", "knowledge");
2580
+ this.indexPath = import_path3.default.join(this.cacheDirectory, "index.json");
2097
2581
  }
2098
2582
  async saveResult(result, options) {
2099
2583
  await import_fs3.promises.mkdir(this.cacheDirectory, { recursive: true });
@@ -2218,8 +2702,8 @@ var LocalKnowledgeStore = class {
2218
2702
  return { document, result };
2219
2703
  }
2220
2704
  getResultDirectoryPath(localDocumentId) {
2221
- const documentsDirectory = import_path2.default.resolve(this.cacheDirectory, "documents");
2222
- const resultDirectoryPath = import_path2.default.resolve(documentsDirectory, localDocumentId);
2705
+ const documentsDirectory = import_path3.default.resolve(this.cacheDirectory, "documents");
2706
+ const resultDirectoryPath = import_path3.default.resolve(documentsDirectory, localDocumentId);
2223
2707
  if (!isPathInsideDirectory(resultDirectoryPath, documentsDirectory)) {
2224
2708
  throw new Error(`Local Knowhere document ID resolves outside the cache: ${localDocumentId}`);
2225
2709
  }
@@ -2253,7 +2737,7 @@ var LocalKnowledgeStore = class {
2253
2737
  }
2254
2738
  };
2255
2739
  function validateLocalDocumentId(localDocumentId) {
2256
- if (!LOCAL_DOCUMENT_ID_PATTERN.test(localDocumentId) || localDocumentId.includes("..") || import_path2.default.basename(localDocumentId) !== localDocumentId) {
2740
+ if (!LOCAL_DOCUMENT_ID_PATTERN.test(localDocumentId) || localDocumentId.includes("..") || import_path3.default.basename(localDocumentId) !== localDocumentId) {
2257
2741
  throw new Error(
2258
2742
  "Local Knowhere document ID must be a safe slug containing only letters, numbers, dots, underscores, or hyphens"
2259
2743
  );
@@ -2261,11 +2745,11 @@ function validateLocalDocumentId(localDocumentId) {
2261
2745
  return localDocumentId;
2262
2746
  }
2263
2747
  function isPathInsideDirectory(targetPath, parentDirectory) {
2264
- const relativePath = import_path2.default.relative(parentDirectory, targetPath);
2265
- return relativePath.length === 0 || !relativePath.startsWith("..") && !import_path2.default.isAbsolute(relativePath);
2748
+ const relativePath = import_path3.default.relative(parentDirectory, targetPath);
2749
+ return relativePath.length === 0 || !relativePath.startsWith("..") && !import_path3.default.isAbsolute(relativePath);
2266
2750
  }
2267
2751
  function createLocalDocumentId(result) {
2268
- const hash = (0, import_crypto.createHash)("sha256").update(result.jobId).update("\0").update(result.manifest.sourceFileName).digest("hex").slice(0, 16);
2752
+ const hash = (0, import_crypto2.createHash)("sha256").update(result.jobId).update("\0").update(result.manifest.sourceFileName).digest("hex").slice(0, 16);
2269
2753
  return `local_${hash}`;
2270
2754
  }
2271
2755
  function countChunkTypes(result) {
@@ -2419,29 +2903,32 @@ var Knowledge = class _Knowledge {
2419
2903
  return this.importJobResult(params);
2420
2904
  }
2421
2905
  async syncParsedDocument(params) {
2906
+ params.signal?.throwIfAborted();
2422
2907
  const normalized = normalizeDocumentReference(params);
2423
2908
  const parsedStorageConfig = this.requireParsedStorageConfig();
2424
2909
  if (normalized.documentId) {
2425
2910
  return this.syncRemoteDocumentToParsedStorage({
2426
2911
  documentId: normalized.documentId,
2427
2912
  revisionKey: params.revisionKey,
2428
- parsedStorageConfig
2913
+ parsedStorageConfig,
2914
+ signal: params.signal,
2915
+ retainResultArchive: params.retainResultArchive
2429
2916
  });
2430
2917
  }
2431
2918
  if (normalized.jobId) {
2432
- const loadedResult = await this.client.jobs.load(normalized.jobId);
2433
- const synced2 = await syncParseResultToParsedDocumentStorage({
2434
- result: loadedResult,
2919
+ const job = await this.client.jobs.get(normalized.jobId);
2920
+ if (!job.isDone || !job.resultUrl)
2921
+ throw new ValidationError("Completed job result archive is unavailable");
2922
+ return syncRemoteResultArchive({
2923
+ documentId: job.documentId ?? normalized.localDocumentId ?? normalized.jobId,
2924
+ revisionKey: params.revisionKey ?? normalized.jobId,
2925
+ jobId: normalized.jobId,
2926
+ resultUrl: job.resultUrl,
2435
2927
  storage: parsedStorageConfig.storage,
2436
- documentId: loadedResult.documentId ?? normalized.localDocumentId ?? normalized.jobId,
2437
- revisionKey: params.revisionKey ?? normalized.jobId
2928
+ limits: parsedStorageConfig.limits,
2929
+ signal: params.signal,
2930
+ retainResultArchive: params.retainResultArchive
2438
2931
  });
2439
- return {
2440
- documentId: synced2.documentId,
2441
- revisionKey: synced2.revisionKey,
2442
- completed: true,
2443
- commit: synced2.commit
2444
- };
2445
2932
  }
2446
2933
  if (!normalized.localDocumentId) {
2447
2934
  throw new ValidationError("localDocumentId, documentId, or jobId is required");
@@ -3139,10 +3626,14 @@ var Knowledge = class _Knowledge {
3139
3626
  parsedStorageConfig
3140
3627
  });
3141
3628
  } catch (error) {
3629
+ const previous = await parsedStorageConfig.storage.readSyncProgress({
3630
+ documentId,
3631
+ revisionKey
3632
+ });
3142
3633
  await parsedStorageConfig.storage.writeSyncProgress({
3634
+ ...previous,
3143
3635
  documentId,
3144
3636
  revisionKey,
3145
- nextChunkPage: 1,
3146
3637
  status: "failed",
3147
3638
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3148
3639
  error: error instanceof Error ? error.message : String(error)
@@ -3152,6 +3643,7 @@ var Knowledge = class _Knowledge {
3152
3643
  void Promise.resolve(scheduler.schedule(task)).catch(() => void 0);
3153
3644
  }
3154
3645
  async syncRemoteDocumentToParsedStorage(params) {
3646
+ params.signal?.throwIfAborted();
3155
3647
  const firstPage = await this.client.documents.listChunks(params.documentId, {
3156
3648
  page: 1,
3157
3649
  pageSize: 1,
@@ -3159,55 +3651,67 @@ var Knowledge = class _Knowledge {
3159
3651
  });
3160
3652
  const revisionKey = params.revisionKey ?? getRemoteRevisionKey(firstPage);
3161
3653
  if (params.revisionKey && getRemoteRevisionKey(firstPage) !== params.revisionKey) {
3162
- return this.syncRemoteDocumentToParsedStorage({
3163
- documentId: params.documentId,
3164
- parsedStorageConfig: params.parsedStorageConfig
3165
- });
3654
+ throw new ValidationError("Published document revision changed before parsed snapshot sync");
3166
3655
  }
3167
3656
  if (firstPage.jobId) {
3168
- try {
3169
- const loadedResult = await this.client.jobs.load(firstPage.jobId);
3170
- const synced = await syncParseResultToParsedDocumentStorage({
3171
- result: loadedResult,
3172
- storage: params.parsedStorageConfig.storage,
3173
- documentId: params.documentId,
3174
- revisionKey,
3175
- source: "resultZip"
3176
- });
3177
- await params.parsedStorageConfig.storage.writeSyncProgress({
3657
+ const job = await this.client.jobs.get(firstPage.jobId).catch((error) => {
3658
+ if (error instanceof NotFoundError) return null;
3659
+ throw error;
3660
+ });
3661
+ if (job?.isDone && job.resultUrl) {
3662
+ if (job.documentId && job.documentId !== params.documentId)
3663
+ throw new ValidationError("Parsed job document identity differs from requested document");
3664
+ return syncRemoteResultArchive({
3178
3665
  documentId: params.documentId,
3179
3666
  revisionKey,
3180
- status: "completed",
3181
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3667
+ jobId: firstPage.jobId,
3668
+ resultUrl: job.resultUrl,
3669
+ storage: params.parsedStorageConfig.storage,
3670
+ limits: params.parsedStorageConfig.limits,
3671
+ signal: params.signal,
3672
+ retainResultArchive: params.retainResultArchive,
3673
+ verifyRevision: async () => {
3674
+ const current = await this.client.documents.listChunks(params.documentId, {
3675
+ page: 1,
3676
+ pageSize: 1,
3677
+ includeAssetUrls: true
3678
+ });
3679
+ if (getRemoteRevisionKey(current) !== revisionKey || current.jobId !== firstPage.jobId)
3680
+ throw new ValidationError(
3681
+ "Published document revision changed during parsed snapshot sync"
3682
+ );
3683
+ }
3182
3684
  });
3183
- return {
3184
- documentId: synced.documentId,
3185
- revisionKey: synced.revisionKey,
3186
- completed: true,
3187
- commit: synced.commit
3188
- };
3189
- } catch {
3190
3685
  }
3191
3686
  }
3192
3687
  return this.syncRemoteDocumentChunksToParsedStorage({
3193
3688
  documentId: params.documentId,
3194
3689
  revisionKey,
3195
3690
  firstPage,
3196
- parsedStorageConfig: params.parsedStorageConfig
3691
+ parsedStorageConfig: params.parsedStorageConfig,
3692
+ signal: params.signal
3197
3693
  });
3198
3694
  }
3199
3695
  async syncRemoteDocumentChunksToParsedStorage(params) {
3696
+ params.signal?.throwIfAborted();
3200
3697
  const limits = getParsedStorageLimits(params.parsedStorageConfig.limits);
3201
3698
  const existingProgress = await params.parsedStorageConfig.storage.readSyncProgress({
3202
3699
  documentId: params.documentId,
3203
3700
  revisionKey: params.revisionKey
3204
3701
  });
3205
- let nextPage = existingProgress?.status === "running" ? existingProgress.nextChunkPage ?? 1 : 1;
3702
+ let nextPage = existingProgress?.status === "running" || existingProgress?.status === "failed" ? existingProgress.nextChunkPage ?? 1 : 1;
3206
3703
  let response = nextPage === 1 && params.firstPage.pagination.pageSize === limits.remotePageSize ? params.firstPage : await this.client.documents.listChunks(params.documentId, {
3207
3704
  page: nextPage,
3208
3705
  pageSize: limits.remotePageSize,
3209
3706
  includeAssetUrls: true
3210
3707
  });
3708
+ validateRemoteReconstructionRevision(
3709
+ params.documentId,
3710
+ params.revisionKey,
3711
+ params.firstPage.jobId,
3712
+ response
3713
+ );
3714
+ params.signal?.throwIfAborted();
3211
3715
  const totalPages = response.pagination.totalPages;
3212
3716
  let syncedPages = 0;
3213
3717
  const startedAt = Date.now();
@@ -3219,6 +3723,13 @@ var Knowledge = class _Knowledge {
3219
3723
  includeAssetUrls: true
3220
3724
  });
3221
3725
  }
3726
+ params.signal?.throwIfAborted();
3727
+ validateRemoteReconstructionRevision(
3728
+ params.documentId,
3729
+ params.revisionKey,
3730
+ params.firstPage.jobId,
3731
+ response
3732
+ );
3222
3733
  await params.parsedStorageConfig.storage.writeObject({
3223
3734
  documentId: params.documentId,
3224
3735
  revisionKey: params.revisionKey,
@@ -3266,7 +3777,23 @@ var Knowledge = class _Knowledge {
3266
3777
  storage: params.parsedStorageConfig.storage,
3267
3778
  documentId: params.documentId,
3268
3779
  revisionKey: params.revisionKey,
3269
- source: "remoteReconstruction"
3780
+ source: "remoteReconstruction",
3781
+ signal: params.signal,
3782
+ beforeCommit: async () => {
3783
+ params.signal?.throwIfAborted();
3784
+ const current = await this.client.documents.listChunks(params.documentId, {
3785
+ page: 1,
3786
+ pageSize: 1,
3787
+ includeAssetUrls: true
3788
+ });
3789
+ params.signal?.throwIfAborted();
3790
+ validateRemoteReconstructionRevision(
3791
+ params.documentId,
3792
+ params.revisionKey,
3793
+ params.firstPage.jobId,
3794
+ current
3795
+ );
3796
+ }
3270
3797
  });
3271
3798
  await params.parsedStorageConfig.storage.writeSyncProgress({
3272
3799
  documentId: params.documentId,
@@ -3338,7 +3865,7 @@ function createLocalDocumentIdForRemote(documentId) {
3338
3865
  if (SAFE_LOCAL_DOCUMENT_ID_PATTERN.test(documentId) && !documentId.includes("..") && !documentId.includes("/") && !documentId.includes("\\")) {
3339
3866
  return documentId;
3340
3867
  }
3341
- const hash = (0, import_crypto2.createHash)("sha256").update(documentId).digest("hex").slice(0, 16);
3868
+ const hash = (0, import_crypto3.createHash)("sha256").update(documentId).digest("hex").slice(0, 16);
3342
3869
  return `remote_${hash}`;
3343
3870
  }
3344
3871
  var defaultParsedStorageScheduler = {
@@ -3558,10 +4085,10 @@ function parseStoredResultChunks(params) {
3558
4085
  }
3559
4086
  function extractStoredChunkRecords(payload) {
3560
4087
  if (Array.isArray(payload)) {
3561
- return payload.filter(isRecord2);
4088
+ return payload.filter(isRecord3);
3562
4089
  }
3563
- if (isRecord2(payload) && Array.isArray(payload.chunks)) {
3564
- return payload.chunks.filter(isRecord2);
4090
+ if (isRecord3(payload) && Array.isArray(payload.chunks)) {
4091
+ return payload.chunks.filter(isRecord3);
3565
4092
  }
3566
4093
  return [];
3567
4094
  }
@@ -3581,7 +4108,7 @@ function readRecordNumber(record, ...keys) {
3581
4108
  }
3582
4109
  function readRecordMetadata(record) {
3583
4110
  const metadata = record.metadata;
3584
- return isRecord2(metadata) ? keysToCamel(metadata) : {};
4111
+ return isRecord3(metadata) ? keysToCamel(metadata) : {};
3585
4112
  }
3586
4113
  function isKnowledgeChunkType(value) {
3587
4114
  return value === "text" || value === "image" || value === "table" || value === "page";
@@ -3680,7 +4207,7 @@ async function applyStoredPageAssetUrls(params) {
3680
4207
  }
3681
4208
  return Promise.all(
3682
4209
  pageAssets.map(async (pageAsset) => {
3683
- if (!isRecord2(pageAsset)) {
4210
+ if (!isRecord3(pageAsset)) {
3684
4211
  return pageAsset;
3685
4212
  }
3686
4213
  const artifactRef = readRecordString(pageAsset, "artifactRef", "artifact_ref");
@@ -3709,7 +4236,7 @@ function getStoredPageAssets(metadata) {
3709
4236
  return pageAssets?.map(normalizeStoredPageAsset);
3710
4237
  }
3711
4238
  function normalizeStoredPageAsset(pageAsset) {
3712
- if (!isRecord2(pageAsset)) {
4239
+ if (!isRecord3(pageAsset)) {
3713
4240
  return pageAsset;
3714
4241
  }
3715
4242
  const normalizedPageAsset = { ...pageAsset };
@@ -3787,7 +4314,7 @@ function parseContinuationCursor(value) {
3787
4314
  }
3788
4315
  try {
3789
4316
  const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
3790
- if (!isRecord2(parsed)) {
4317
+ if (!isRecord3(parsed)) {
3791
4318
  return null;
3792
4319
  }
3793
4320
  const documentId = parsed.documentId;
@@ -3825,11 +4352,11 @@ function inferContentTypeFromPath2(filePath) {
3825
4352
  if (extension === "html" || extension === "htm") return "text/html; charset=utf-8";
3826
4353
  return "application/octet-stream";
3827
4354
  }
3828
- function isRecord2(value) {
4355
+ function isRecord3(value) {
3829
4356
  return typeof value === "object" && value !== null && !Array.isArray(value);
3830
4357
  }
3831
4358
  function isDocumentChunkArray(value) {
3832
- return Array.isArray(value) && value.every(isRecord2);
4359
+ return Array.isArray(value) && value.every(isRecord3);
3833
4360
  }
3834
4361
  function indexChunks(result) {
3835
4362
  return result.chunks.map((chunk, index) => {
@@ -4132,17 +4659,24 @@ function toRemoteSearchResult(result, documentByServerId) {
4132
4659
  sourceFileName: result.source.sourceFileName ?? void 0
4133
4660
  };
4134
4661
  }
4662
+ function validateRemoteReconstructionRevision(documentId, revisionKey, jobId, response) {
4663
+ if (response.documentId !== documentId || getRemoteRevisionKey(response) !== revisionKey || (response.jobId ?? void 0) !== (jobId ?? void 0)) {
4664
+ throw new ValidationError("Published document revision changed during remote reconstruction");
4665
+ }
4666
+ }
4135
4667
 
4136
4668
  // src/client.ts
4669
+ init_constants();
4670
+ init_utils();
4137
4671
  function inferFileName(file, explicitFileName) {
4138
4672
  if (explicitFileName) {
4139
4673
  return explicitFileName;
4140
4674
  }
4141
4675
  if (typeof file === "string") {
4142
- return import_path3.default.basename(file);
4676
+ return import_path4.default.basename(file);
4143
4677
  }
4144
4678
  if (isReadStream2(file) && typeof file.path === "string") {
4145
- return import_path3.default.basename(file.path);
4679
+ return import_path4.default.basename(file.path);
4146
4680
  }
4147
4681
  return void 0;
4148
4682
  }
@@ -4288,9 +4822,9 @@ var Knowhere = class {
4288
4822
  };
4289
4823
 
4290
4824
  // src/storage/disk-parsed-document-storage.ts
4291
- var import_crypto3 = require("crypto");
4825
+ var import_crypto4 = require("crypto");
4292
4826
  var import_fs4 = require("fs");
4293
- var import_path4 = __toESM(require("path"));
4827
+ var import_path5 = __toESM(require("path"));
4294
4828
  var syncProgressPath = ".knowhere-sdk/sync-progress.json";
4295
4829
  var DiskParsedDocumentStorage = class {
4296
4830
  rootDirectory;
@@ -4319,7 +4853,7 @@ var DiskParsedDocumentStorage = class {
4319
4853
  }
4320
4854
  async writeObject(params) {
4321
4855
  const objectPath = this.getObjectPath(params);
4322
- await import_fs4.promises.mkdir(import_path4.default.dirname(objectPath), { recursive: true });
4856
+ await import_fs4.promises.mkdir(import_path5.default.dirname(objectPath), { recursive: true });
4323
4857
  await import_fs4.promises.writeFile(objectPath, params.body);
4324
4858
  await this.writeObjectMetadata(objectPath, {
4325
4859
  contentType: params.contentType,
@@ -4378,14 +4912,14 @@ var DiskParsedDocumentStorage = class {
4378
4912
  }
4379
4913
  getObjectPath(params) {
4380
4914
  const revisionDirectory = this.getRevisionDirectory(params.documentId, params.revisionKey);
4381
- const objectPath = import_path4.default.resolve(revisionDirectory, normalizeRelativeStoragePath(params.path));
4915
+ const objectPath = import_path5.default.resolve(revisionDirectory, normalizeRelativeStoragePath(params.path));
4382
4916
  if (!isPathInsideDirectory2(objectPath, revisionDirectory)) {
4383
4917
  throw new Error(`Parsed document object path resolves outside storage: ${params.path}`);
4384
4918
  }
4385
4919
  return objectPath;
4386
4920
  }
4387
4921
  getRevisionDirectory(documentId, revisionKey) {
4388
- return import_path4.default.join(this.rootDirectory, hashPathPart(documentId), hashPathPart(revisionKey));
4922
+ return import_path5.default.join(this.rootDirectory, hashPathPart(documentId), hashPathPart(revisionKey));
4389
4923
  }
4390
4924
  async readOptionalJson(params) {
4391
4925
  const object = await this.readObject(params);
@@ -4421,7 +4955,7 @@ function hashPathPart(value) {
4421
4955
  if (normalized.length === 0) {
4422
4956
  throw new Error("Parsed document storage path part must be a non-empty value.");
4423
4957
  }
4424
- return (0, import_crypto3.createHash)("sha256").update(normalized).digest("hex").slice(0, 24);
4958
+ return (0, import_crypto4.createHash)("sha256").update(normalized).digest("hex").slice(0, 24);
4425
4959
  }
4426
4960
  function normalizeRelativeStoragePath(value) {
4427
4961
  const normalized = value.replaceAll("\\", "/").replace(/^\.\/+/, "");
@@ -4431,8 +4965,8 @@ function normalizeRelativeStoragePath(value) {
4431
4965
  return normalized;
4432
4966
  }
4433
4967
  function isPathInsideDirectory2(filePath, directory) {
4434
- const relative = import_path4.default.relative(directory, filePath);
4435
- return relative === "" || !relative.startsWith("..") && !import_path4.default.isAbsolute(relative);
4968
+ const relative = import_path5.default.relative(directory, filePath);
4969
+ return relative === "" || !relative.startsWith("..") && !import_path5.default.isAbsolute(relative);
4436
4970
  }
4437
4971
  function toFileUrl(filePath) {
4438
4972
  return `file://${filePath}`;
@@ -4440,6 +4974,9 @@ function toFileUrl(filePath) {
4440
4974
  function isMissingFileError3(error) {
4441
4975
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
4442
4976
  }
4977
+
4978
+ // src/index.ts
4979
+ init_parsed_document_storage();
4443
4980
  // Annotate the CommonJS export names for ESM import in node:
4444
4981
  0 && (module.exports = {
4445
4982
  APIError,
@@ -4461,6 +4998,7 @@ function isMissingFileError3(error) {
4461
4998
  NODE_SDK_DOCUMENT_METADATA_DEFAULTS,
4462
4999
  NetworkError,
4463
5000
  NotFoundError,
5001
+ ParsedDocumentArchiveError,
4464
5002
  PaymentRequiredError,
4465
5003
  PermissionDeniedError,
4466
5004
  PollingTimeoutError,