@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.
@@ -0,0 +1,139 @@
1
+ import {
2
+ ParsedDocumentArchiveError,
3
+ createDownloadIdentity,
4
+ isSnapshotPath,
5
+ normalizeResultPath
6
+ } from "./chunk-4PWLXJGH.mjs";
7
+
8
+ // src/storage/sequential-parsed-document-archive.ts
9
+ import { Readable, Transform } from "stream";
10
+ import { pipeline } from "stream/promises";
11
+ import { mkdtemp, open, rm } from "fs/promises";
12
+ import { tmpdir } from "os";
13
+ import { join } from "path";
14
+ import { crc32 } from "zlib";
15
+ import { ZipReader, Reader } from "@zip.js/zip.js";
16
+ import Parse from "unzipper/lib/parse.js";
17
+ async function openSequentialArchive(params, identity) {
18
+ const response = await fetch(params.resultUrl, { signal: params.signal });
19
+ if (!response.ok || !response.body)
20
+ throw new Error(`Parsed result download failed (HTTP ${response.status}).`);
21
+ if (createDownloadIdentity(params.jobId, response) !== identity) {
22
+ await response.body.cancel();
23
+ throw new Error("Parsed result archive identity changed before sequential download.");
24
+ }
25
+ let directory;
26
+ try {
27
+ directory = await mkdtemp(join(tmpdir(), "knowhere-sync-"));
28
+ } catch (error) {
29
+ await response.body.cancel();
30
+ throw new ParsedDocumentArchiveError(error);
31
+ }
32
+ let file;
33
+ try {
34
+ file = await open(join(directory, "archive.zip"), "w+");
35
+ } catch (error) {
36
+ await response.body.cancel();
37
+ await rm(directory, { recursive: true, force: true });
38
+ throw new ParsedDocumentArchiveError(error);
39
+ }
40
+ const input = Readable.fromWeb(response.body);
41
+ const parser = Parse({ forceStream: true });
42
+ const checksums = /* @__PURE__ */ new Map();
43
+ let archiveSize = 0;
44
+ const spool = new Transform({
45
+ transform(chunk, _encoding, callback) {
46
+ archiveSize += chunk.length;
47
+ void file.writeFile(chunk).then(
48
+ () => {
49
+ callback(null, chunk);
50
+ },
51
+ (error) => {
52
+ callback(new ParsedDocumentArchiveError(error));
53
+ }
54
+ );
55
+ }
56
+ });
57
+ const completed = pipeline(input, spool, parser, { signal: params.signal });
58
+ void completed.catch(() => void 0);
59
+ return {
60
+ identity,
61
+ downloadIdentity: identity,
62
+ canSkipEntries: false,
63
+ entries: (async function* () {
64
+ for await (const value of parser) {
65
+ const entry = value;
66
+ if (entry.type === "Directory" || !isSnapshotPath(entry.path)) {
67
+ await entry.autodrain().promise();
68
+ continue;
69
+ }
70
+ const path = normalizeResultPath(entry.path);
71
+ yield {
72
+ path,
73
+ read: () => {
74
+ let checksum = 0;
75
+ let size = 0;
76
+ return Readable.toWeb(entry).pipeThrough(
77
+ new TransformStream({
78
+ transform(chunk, controller) {
79
+ checksum = crc32(chunk, checksum);
80
+ size += chunk.length;
81
+ controller.enqueue(chunk);
82
+ },
83
+ flush() {
84
+ checksums.set(path, { checksum, size });
85
+ }
86
+ })
87
+ );
88
+ }
89
+ };
90
+ }
91
+ await completed;
92
+ const reader = new ZipReader(new DiskArchiveReader(file, archiveSize), {
93
+ useWebWorkers: false
94
+ });
95
+ try {
96
+ const files = (await reader.getEntries()).filter(
97
+ (entry) => !entry.directory && isSnapshotPath(entry.filename)
98
+ );
99
+ if (files.length !== checksums.size)
100
+ throw new Error("Parsed result ZIP directory does not match sequential entries.");
101
+ for (const entry of files) {
102
+ const actual = checksums.get(normalizeResultPath(entry.filename));
103
+ if (!actual || actual.checksum !== entry.signature || actual.size !== entry.uncompressedSize)
104
+ throw new Error(`Invalid parsed result ZIP checksum or size: ${entry.filename}`);
105
+ }
106
+ } finally {
107
+ await reader.close();
108
+ }
109
+ })(),
110
+ close: async () => {
111
+ input.destroy();
112
+ spool.destroy();
113
+ parser.destroy();
114
+ await completed.catch(() => void 0);
115
+ try {
116
+ await file.close();
117
+ } finally {
118
+ await rm(directory, { recursive: true, force: true });
119
+ }
120
+ }
121
+ };
122
+ }
123
+ var DiskArchiveReader = class extends Reader {
124
+ constructor(file, size) {
125
+ super(file);
126
+ this.file = file;
127
+ this.size = size;
128
+ }
129
+ file;
130
+ async readUint8Array(index, length) {
131
+ const body = new Uint8Array(length);
132
+ const { bytesRead } = await this.file.read(body, 0, length, index);
133
+ if (bytesRead !== length) throw new Error("Truncated parsed result ZIP.");
134
+ return body;
135
+ }
136
+ };
137
+ export {
138
+ openSequentialArchive
139
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontos-ai/knowhere-sdk",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "Official Node.js SDK for Knowhere document parsing API",
5
5
  "keywords": [
6
6
  "knowhere",
@@ -43,14 +43,17 @@
43
43
  "pnpm": ">=9.0.0"
44
44
  },
45
45
  "dependencies": {
46
+ "@zip.js/zip.js": "^2.24.0",
46
47
  "axios": "^1.15.2",
47
- "jszip": "^3.10.0"
48
+ "jszip": "^3.10.0",
49
+ "unzipper": "^0.12.5"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@changesets/changelog-github": "^0.6.0",
51
53
  "@changesets/cli": "^2.30.0",
52
54
  "@eslint/js": "^10.0.1",
53
55
  "@types/node": "^25.2.3",
56
+ "@types/unzipper": "^0.10.11",
54
57
  "@typescript-eslint/eslint-plugin": "^8.55.0",
55
58
  "@typescript-eslint/parser": "^8.55.0",
56
59
  "@vitest/coverage-v8": "^4.0.18",