@llamaindex/liteparse 2.13.1 → 2.14.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/lib.js CHANGED
@@ -1,347 +1,678 @@
1
- import { native, } from "./native.js";
2
- // ---------------------------------------------------------------------------
3
- // LiteParse class
4
- // ---------------------------------------------------------------------------
5
- export class LiteParse {
6
- _native;
7
- _config;
8
- constructor(userConfig = {}) {
9
- const nativeConfig = {
10
- ocrLanguage: userConfig.ocrLanguage,
11
- ocrEnabled: userConfig.ocrEnabled,
12
- ocrServerUrl: userConfig.ocrServerUrl,
13
- ocrServerHeaders: userConfig.ocrServerHeaders,
14
- tessdataPath: userConfig.tessdataPath,
15
- maxPages: userConfig.maxPages,
16
- targetPages: userConfig.targetPages,
17
- extractScreenshots: userConfig.extractScreenshots,
18
- continueOnPageError: userConfig.continueOnPageError,
19
- dpi: userConfig.dpi,
20
- outputFormat: userConfig.outputFormat,
21
- imageMode: userConfig.imageMode,
22
- extractImages: userConfig.extractImages,
23
- imageOutputDir: userConfig.imageOutputDir,
24
- extractLinks: userConfig.extractLinks,
25
- keepHeadersFooters: userConfig.keepHeadersFooters,
26
- extractAnnotations: userConfig.extractAnnotations,
27
- extractFormFields: userConfig.extractFormFields,
28
- extractStructureTree: userConfig.extractStructureTree,
29
- extractBlocks: userConfig.extractBlocks,
30
- extractXfaPackets: userConfig.extractXfaPackets,
31
- extractDocumentMetadata: userConfig.extractDocumentMetadata,
32
- extractContentBounds: userConfig.extractContentBounds,
33
- detectScreenshotRects: userConfig.detectScreenshotRects,
34
- renderFormFields: userConfig.renderFormFields,
35
- preserveVerySmallText: userConfig.preserveVerySmallText,
36
- password: userConfig.password,
37
- quiet: userConfig.quiet,
38
- numWorkers: userConfig.numWorkers,
39
- ocrFailureFatal: userConfig.ocrFailureFatal,
40
- ocrHedgeDelaysMs: userConfig.ocrHedgeDelaysMs,
41
- emitWordBoxes: userConfig.emitWordBoxes,
42
- extractTextMetadata: userConfig.extractTextMetadata,
43
- cropBox: userConfig.cropBox,
44
- skipDiagonalText: userConfig.skipDiagonalText,
45
- includeComplexity: userConfig.includeComplexity,
46
- extractVectorGraphics: userConfig.extractVectorGraphics,
47
- };
48
- this._native = new native.LiteParse(nativeConfig);
49
- // Read back the resolved config from the native side
50
- const resolved = this._native.config;
51
- this._config = {
52
- ocrLanguage: resolved.ocrLanguage ?? "eng",
53
- ocrEnabled: resolved.ocrEnabled ?? true,
54
- ocrServerUrl: resolved.ocrServerUrl ?? undefined,
55
- ocrServerHeaders: resolved.ocrServerHeaders ?? undefined,
56
- tessdataPath: resolved.tessdataPath ?? undefined,
57
- maxPages: resolved.maxPages ?? 1000,
58
- targetPages: resolved.targetPages ?? undefined,
59
- extractScreenshots: resolved.extractScreenshots ?? false,
60
- continueOnPageError: resolved.continueOnPageError ?? false,
61
- dpi: resolved.dpi ?? 150,
62
- outputFormat: resolved.outputFormat ?? "json",
63
- imageMode: resolved.imageMode ?? "placeholder",
64
- extractImages: resolved.extractImages ?? false,
65
- imageOutputDir: resolved.imageOutputDir ?? undefined,
66
- extractLinks: resolved.extractLinks ?? true,
67
- keepHeadersFooters: resolved.keepHeadersFooters ?? false,
68
- extractAnnotations: resolved.extractAnnotations ?? false,
69
- extractFormFields: resolved.extractFormFields ?? false,
70
- extractStructureTree: resolved.extractStructureTree ?? false,
71
- extractBlocks: resolved.extractBlocks ?? false,
72
- extractXfaPackets: resolved.extractXfaPackets ?? false,
73
- extractDocumentMetadata: resolved.extractDocumentMetadata ?? false,
74
- extractContentBounds: resolved.extractContentBounds ?? false,
75
- detectScreenshotRects: resolved.detectScreenshotRects ?? false,
76
- renderFormFields: resolved.renderFormFields ?? false,
77
- preserveVerySmallText: resolved.preserveVerySmallText ?? false,
78
- password: resolved.password ?? undefined,
79
- quiet: resolved.quiet ?? false,
80
- numWorkers: resolved.numWorkers ?? 1,
81
- ocrFailureFatal: resolved.ocrFailureFatal ?? true,
82
- ocrHedgeDelaysMs: resolved.ocrHedgeDelaysMs ?? [],
83
- emitWordBoxes: resolved.emitWordBoxes ?? false,
84
- extractTextMetadata: resolved.extractTextMetadata ?? false,
85
- cropBox: resolved.cropBox ?? undefined,
86
- skipDiagonalText: resolved.skipDiagonalText ?? false,
87
- includeComplexity: resolved.includeComplexity ?? false,
88
- extractVectorGraphics: resolved.extractVectorGraphics ?? false,
89
- };
1
+ // src/native.ts
2
+ import { createRequire } from "module";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ var require2 = createRequire(import.meta.url);
6
+ var __dirname = dirname(fileURLToPath(import.meta.url));
7
+ function loadNative() {
8
+ const triples = {
9
+ "darwin-x64": "@llamaindex/liteparse-darwin-x64",
10
+ "darwin-arm64": "@llamaindex/liteparse-darwin-arm64",
11
+ "linux-x64-gnu": "@llamaindex/liteparse-linux-x64-gnu",
12
+ "linux-x64-musl": "@llamaindex/liteparse-linux-x64-musl",
13
+ "linux-arm64-gnu": "@llamaindex/liteparse-linux-arm64-gnu",
14
+ "linux-arm64-musl": "@llamaindex/liteparse-linux-arm64-musl",
15
+ "win32-x64-msvc": "@llamaindex/liteparse-win32-x64-msvc",
16
+ "win32-arm64-msvc": "@llamaindex/liteparse-win32-arm64-msvc"
17
+ };
18
+ const platform = process.platform;
19
+ const arch = process.arch;
20
+ const candidates = [];
21
+ if (platform === "linux") {
22
+ candidates.push(`${platform}-${arch}-gnu`);
23
+ candidates.push(`${platform}-${arch}-musl`);
24
+ } else if (platform === "win32") {
25
+ candidates.push(`${platform}-${arch}-msvc`);
26
+ } else {
27
+ candidates.push(`${platform}-${arch}`);
28
+ }
29
+ for (const key of candidates) {
30
+ const pkg = triples[key];
31
+ if (pkg) {
32
+ try {
33
+ return require2(pkg);
34
+ } catch {
35
+ }
90
36
  }
91
- async parse(input) {
92
- // Convert Uint8Array to Buffer for the native side
93
- const nativeInput = typeof input === "string" ? input : Buffer.from(input);
94
- const result = await this._native.parse(nativeInput);
95
- return toParseResult(result);
37
+ }
38
+ const searchDirs = [__dirname, join(__dirname, ".."), join(__dirname, "..", "..")];
39
+ const fileNames = [
40
+ ...candidates.map((c) => `liteparse.${c}.node`),
41
+ `liteparse.${platform}-${arch}.node`,
42
+ "liteparse.node"
43
+ ];
44
+ for (const dir of searchDirs) {
45
+ for (const fileName of fileNames) {
46
+ try {
47
+ return require2(join(dir, fileName));
48
+ } catch {
49
+ }
96
50
  }
97
- /**
98
- * Parse a document in bounded-memory page batches of `batchSize` pages.
99
- *
100
- * Each yielded result is independent and becomes collectible once the caller
101
- * advances the iterator, so a consumer that does not retain batches never
102
- * holds more than one batch of pages in memory. A non-PDF source is
103
- * converted once when the iterator starts, not once per batch; its temporary
104
- * file is released when iteration ends — including an early `break` or
105
- * `throw`, which run the generator's cleanup.
106
- *
107
- * Cross-page passes see only the pages in their own batch, so repeated
108
- * header/footer removal and image deduplication are batch-local and the
109
- * output can differ from `parse()`. Prefer `parse()` unless the size of the
110
- * materialized result is the problem.
111
- *
112
- * As with any async generator, work starts on the first `next()` call, so
113
- * errors (an unreadable file, or a parser configured with `targetPages` —
114
- * ambiguous with generated batch ranges) surface on the first iteration
115
- * rather than when `parseBatches()` itself is called.
116
- */
117
- async *parseBatches(input, options = {}) {
118
- const nativeInput = typeof input === "string" ? input : Buffer.from(input);
119
- const session = await this._native.openBatchSession(nativeInput, options.batchSize);
120
- try {
121
- const totalPages = session.totalPages;
122
- for (;;) {
123
- const batch = await session.nextBatch();
124
- if (batch == null) {
125
- return;
126
- }
127
- yield {
128
- startPage: batch.startPage,
129
- endPage: batch.endPage,
130
- totalPages,
131
- result: toParseResult(batch.result),
132
- };
133
- }
134
- }
135
- finally {
136
- // Frees the session's converted-PDF temp file now instead of at GC —
137
- // this runs on normal exhaustion and when the consumer abandons the
138
- // loop early.
139
- await session.close();
140
- }
51
+ }
52
+ throw new Error(
53
+ `Failed to load native module for ${platform}-${arch}. Ensure the correct optional dependency is installed.`
54
+ );
55
+ }
56
+ var native = loadNative();
57
+
58
+ // src/pool.ts
59
+ import { fork } from "child_process";
60
+ import { fileURLToPath as fileURLToPath2 } from "url";
61
+ var ParseTimeoutError = class extends Error {
62
+ source;
63
+ timeoutMs;
64
+ constructor(message, source, timeoutMs) {
65
+ super(message);
66
+ this.name = "ParseTimeoutError";
67
+ this.source = source;
68
+ this.timeoutMs = timeoutMs;
69
+ }
70
+ };
71
+ var WorkerTimeout = class extends Error {
72
+ };
73
+ var WorkerCrashed = class extends Error {
74
+ };
75
+ var WORKER_PATH = fileURLToPath2(new URL("./pool-worker.js", import.meta.url));
76
+ function reviveBuffers(result) {
77
+ for (const image of result.images ?? []) {
78
+ if (image.bytes && !Buffer.isBuffer(image.bytes)) {
79
+ const b = image.bytes;
80
+ image.bytes = Buffer.from(b.buffer, b.byteOffset, b.byteLength);
141
81
  }
142
- /**
143
- * Parse from pre-extracted pages, skipping PDFium text extraction. Runs only
144
- * grid projection + the configured output formatter, so the caller's own
145
- * text-extraction / font-recovery owns the text content. Synchronous: no
146
- * PDFium load and no OCR on this path.
147
- */
148
- parsePages(pages) {
149
- const nativePages = pages.map((p) => ({
150
- pageNumber: p.pageNumber,
151
- pageWidth: p.pageWidth,
152
- pageHeight: p.pageHeight,
153
- textItems: p.textItems,
154
- graphics: p.graphics,
155
- }));
156
- const result = this._native.parsePages(nativePages);
157
- return toParseResult(result);
82
+ }
83
+ for (const shot of result.screenshots ?? []) {
84
+ if (shot.imageBuffer && !Buffer.isBuffer(shot.imageBuffer)) {
85
+ const b = shot.imageBuffer;
86
+ shot.imageBuffer = Buffer.from(b.buffer, b.byteOffset, b.byteLength);
158
87
  }
159
- /**
160
- * Determine per-page complexity without running a full parse. Returns one
161
- * entry per page with signals and a `needsOcr` verdict — a cheap pre-OCR
162
- * check to decide whether a document needs advanced parsing.
163
- */
164
- async isComplex(input) {
165
- const nativeInput = typeof input === "string" ? input : Buffer.from(input);
166
- const stats = await this._native.isComplex(nativeInput);
167
- return stats.map(toComplexity);
88
+ }
89
+ return result;
90
+ }
91
+ var WorkerHandle = class {
92
+ child;
93
+ readyPromise;
94
+ pending = null;
95
+ dead = false;
96
+ constructor(config) {
97
+ this.child = fork(WORKER_PATH, [], {
98
+ serialization: "advanced",
99
+ // stdout/stderr inherited: parse logs and crash traces stay visible.
100
+ stdio: ["ignore", "inherit", "inherit", "ipc"]
101
+ });
102
+ let readyResolve;
103
+ let readyReject;
104
+ this.readyPromise = new Promise((resolve, reject) => {
105
+ readyResolve = resolve;
106
+ readyReject = reject;
107
+ });
108
+ this.readyPromise.catch(() => {
109
+ });
110
+ this.child.on("message", (msg) => {
111
+ if (msg.type === "ready") {
112
+ readyResolve();
113
+ if (this.pending === null) this.idle();
114
+ } else if (msg.type === "initError") {
115
+ readyReject(new Error(msg.message));
116
+ } else if (this.pending) {
117
+ const { resolve, reject } = this.pending;
118
+ this.pending = null;
119
+ this.idle();
120
+ if (msg.type === "ok") resolve(reviveBuffers(msg.result));
121
+ else reject(new WorkerCrashed(msg.message));
122
+ }
123
+ });
124
+ const onGone = (cause) => {
125
+ this.dead = true;
126
+ readyReject(new WorkerCrashed(cause));
127
+ if (this.pending) {
128
+ const { reject } = this.pending;
129
+ this.pending = null;
130
+ reject(new WorkerCrashed(cause));
131
+ }
132
+ };
133
+ this.child.on("error", (e) => onGone(e.message));
134
+ this.child.on(
135
+ "exit",
136
+ (code, signal) => onGone(`worker exited (code=${code}, signal=${signal})`)
137
+ );
138
+ this.child.send({ type: "init", config });
139
+ }
140
+ /** Resolves when the worker's native parser is constructed. Init time
141
+ * never counts toward the parse deadline — the deadline is a promise about
142
+ * parsing, not about process startup. */
143
+ ready() {
144
+ return this.readyPromise;
145
+ }
146
+ /** An idle pool must not hold the parent's event loop open. */
147
+ idle() {
148
+ this.child.unref();
149
+ this.child.channel?.unref();
150
+ }
151
+ request(payload, timeoutMs) {
152
+ if (this.dead) {
153
+ return Promise.reject(new WorkerCrashed("worker already exited"));
168
154
  }
169
- async screenshot(input, pageNumbers) {
170
- const nativeInput = typeof input === "string" ? input : Buffer.from(input);
171
- const results = await this._native.screenshot(nativeInput, pageNumbers ?? null);
172
- return results.map((r) => ({
173
- pageNum: r.pageNum,
174
- width: r.width,
175
- height: r.height,
176
- imageBuffer: r.imageBuffer,
177
- isSolidFill: r.isSolidFill,
178
- rects: r.rects,
179
- }));
155
+ this.child.ref();
156
+ this.child.channel?.ref();
157
+ return new Promise((resolve, reject) => {
158
+ let timer;
159
+ const settle = (fn) => (value) => {
160
+ if (timer !== void 0) clearTimeout(timer);
161
+ fn(value);
162
+ };
163
+ this.pending = {
164
+ resolve: settle(resolve),
165
+ reject: settle(reject)
166
+ };
167
+ if (timeoutMs !== void 0) {
168
+ timer = setTimeout(() => {
169
+ if (this.pending) {
170
+ const { reject: rejectPending } = this.pending;
171
+ this.pending = null;
172
+ rejectPending(new WorkerTimeout());
173
+ }
174
+ }, timeoutMs);
175
+ }
176
+ this.child.send({ type: "parse", payload });
177
+ });
178
+ }
179
+ kill() {
180
+ this.dead = true;
181
+ this.child.kill("SIGKILL");
182
+ }
183
+ /** Graceful shutdown; escalates to SIGKILL if the worker doesn't exit. */
184
+ stop() {
185
+ if (this.dead) return;
186
+ this.dead = true;
187
+ try {
188
+ this.child.send({ type: "stop" });
189
+ } catch {
180
190
  }
181
- getConfig() {
182
- return { ...this._config };
191
+ const escalate = setTimeout(() => this.child.kill("SIGKILL"), 5e3);
192
+ escalate.unref();
193
+ this.child.once("exit", () => clearTimeout(escalate));
194
+ this.idle();
195
+ }
196
+ };
197
+ var WorkerPool = class {
198
+ config;
199
+ timeoutMs;
200
+ workers = /* @__PURE__ */ new Set();
201
+ idle = [];
202
+ waiters = [];
203
+ closed = false;
204
+ constructor(config, poolSize, parseTimeoutMs) {
205
+ if (!Number.isInteger(poolSize) || poolSize < 1) {
206
+ throw new Error("poolSize must be an integer >= 1");
183
207
  }
184
- }
185
- function toComplexity(s) {
186
- return {
187
- pageNumber: s.pageNumber,
188
- textLength: s.textLength,
189
- textCoverage: s.textCoverage,
190
- hasSubstantialImages: s.hasSubstantialImages,
191
- imageBlockCount: s.imageBlockCount,
192
- imageCoverage: s.imageCoverage,
193
- largestImageCoverage: s.largestImageCoverage,
194
- fullPageImage: s.fullPageImage,
195
- uncoveredVectorArea: s.uncoveredVectorArea ?? undefined,
196
- isGarbled: s.isGarbled,
197
- pageArea: s.pageArea,
198
- needsOcr: s.needsOcr,
199
- reasons: s.reasons,
200
- layout: s.layout
201
- ? {
202
- columnCount: s.layout.columnCount,
203
- ruledTableCount: s.layout.ruledTableCount,
204
- ruledTableCoverage: s.layout.ruledTableCoverage,
205
- textTableRunCount: s.layout.textTableRunCount,
206
- figureCount: s.layout.figureCount,
207
- figureCoverage: s.layout.figureCoverage,
208
- isComplex: s.layout.isComplex,
209
- reasons: s.layout.reasons,
210
- }
211
- : undefined,
208
+ if (parseTimeoutMs !== void 0 && !(parseTimeoutMs > 0)) {
209
+ throw new Error("parseTimeoutMs must be > 0");
210
+ }
211
+ this.config = config;
212
+ this.timeoutMs = parseTimeoutMs;
213
+ for (let i = 0; i < poolSize; i++) {
214
+ this.spawnWorker();
215
+ }
216
+ }
217
+ spawnWorker() {
218
+ const worker = new WorkerHandle(this.config);
219
+ this.workers.add(worker);
220
+ this.release(worker);
221
+ }
222
+ acquire() {
223
+ const worker = this.idle.pop();
224
+ if (worker !== void 0) return Promise.resolve(worker);
225
+ return new Promise(
226
+ (resolve, reject) => this.waiters.push({ resolve, reject })
227
+ );
228
+ }
229
+ release(worker) {
230
+ if (this.closed) {
231
+ this.workers.delete(worker);
232
+ worker.stop();
233
+ return;
234
+ }
235
+ const waiter = this.waiters.shift();
236
+ if (waiter !== void 0) waiter.resolve(worker);
237
+ else this.idle.push(worker);
238
+ }
239
+ retire(worker) {
240
+ worker.kill();
241
+ this.workers.delete(worker);
242
+ if (!this.closed) this.spawnWorker();
243
+ }
244
+ /** Run one parse on an idle worker.
245
+ *
246
+ * Waits for a free worker first; `parseTimeoutMs` bounds the parse itself,
247
+ * not the wait. */
248
+ async parse(payload, source) {
249
+ if (this.closed) throw new Error("parser pool is closed");
250
+ const worker = await this.acquire();
251
+ try {
252
+ await worker.ready();
253
+ const result = await worker.request(payload, this.timeoutMs);
254
+ this.release(worker);
255
+ return result;
256
+ } catch (e) {
257
+ this.retire(worker);
258
+ if (e instanceof WorkerTimeout) {
259
+ throw new ParseTimeoutError(
260
+ `parse of ${source} exceeded ${this.timeoutMs}ms; the worker process was killed`,
261
+ source,
262
+ this.timeoutMs
263
+ );
264
+ }
265
+ if (e instanceof WorkerCrashed) {
266
+ throw new Error(
267
+ `liteparse worker process died while parsing ${source}: ${e.message}`
268
+ );
269
+ }
270
+ throw e;
271
+ }
272
+ }
273
+ /** Resolves when every worker is initialized. Optional — the first parse
274
+ * per worker waits for init anyway. */
275
+ async warmUp() {
276
+ await Promise.all([...this.workers].map((w) => w.ready()));
277
+ }
278
+ /** Shut down all workers. Idempotent. Busy workers are stopped as their
279
+ * in-flight parses finish. */
280
+ close() {
281
+ if (this.closed) return;
282
+ this.closed = true;
283
+ for (const waiter of this.waiters.splice(0)) {
284
+ waiter.reject(new Error("parser pool is closed"));
285
+ }
286
+ for (const worker of this.idle.splice(0)) {
287
+ this.workers.delete(worker);
288
+ worker.stop();
289
+ }
290
+ }
291
+ };
292
+
293
+ // src/lib.ts
294
+ var LiteParse = class {
295
+ _native;
296
+ _config;
297
+ _pool = null;
298
+ constructor(userConfig = {}) {
299
+ const nativeConfig = {
300
+ ocrLanguage: userConfig.ocrLanguage,
301
+ ocrEnabled: userConfig.ocrEnabled,
302
+ ocrServerUrl: userConfig.ocrServerUrl,
303
+ ocrServerHeaders: userConfig.ocrServerHeaders,
304
+ tessdataPath: userConfig.tessdataPath,
305
+ maxPages: userConfig.maxPages,
306
+ targetPages: userConfig.targetPages,
307
+ extractScreenshots: userConfig.extractScreenshots,
308
+ continueOnPageError: userConfig.continueOnPageError,
309
+ dpi: userConfig.dpi,
310
+ outputFormat: userConfig.outputFormat,
311
+ imageMode: userConfig.imageMode,
312
+ extractImages: userConfig.extractImages,
313
+ imageOutputDir: userConfig.imageOutputDir,
314
+ extractLinks: userConfig.extractLinks,
315
+ keepHeadersFooters: userConfig.keepHeadersFooters,
316
+ extractAnnotations: userConfig.extractAnnotations,
317
+ extractFormFields: userConfig.extractFormFields,
318
+ extractStructureTree: userConfig.extractStructureTree,
319
+ extractBlocks: userConfig.extractBlocks,
320
+ extractXfaPackets: userConfig.extractXfaPackets,
321
+ extractDocumentMetadata: userConfig.extractDocumentMetadata,
322
+ extractContentBounds: userConfig.extractContentBounds,
323
+ detectScreenshotRects: userConfig.detectScreenshotRects,
324
+ renderFormFields: userConfig.renderFormFields,
325
+ preserveVerySmallText: userConfig.preserveVerySmallText,
326
+ password: userConfig.password,
327
+ quiet: userConfig.quiet,
328
+ numWorkers: userConfig.numWorkers,
329
+ ocrFailureFatal: userConfig.ocrFailureFatal,
330
+ ocrHedgeDelaysMs: userConfig.ocrHedgeDelaysMs,
331
+ emitWordBoxes: userConfig.emitWordBoxes,
332
+ extractTextMetadata: userConfig.extractTextMetadata,
333
+ cropBox: userConfig.cropBox,
334
+ skipDiagonalText: userConfig.skipDiagonalText,
335
+ includeComplexity: userConfig.includeComplexity,
336
+ extractVectorGraphics: userConfig.extractVectorGraphics
337
+ };
338
+ this._native = new native.LiteParse(nativeConfig);
339
+ if (userConfig.parseTimeoutMs !== void 0 && userConfig.poolSize === void 0) {
340
+ throw new Error(
341
+ "parseTimeoutMs requires poolSize"
342
+ );
343
+ }
344
+ if (userConfig.poolSize !== void 0) {
345
+ this._pool = new WorkerPool(
346
+ nativeConfig,
347
+ userConfig.poolSize,
348
+ userConfig.parseTimeoutMs
349
+ );
350
+ }
351
+ const resolved = this._native.config;
352
+ this._config = {
353
+ ocrLanguage: resolved.ocrLanguage ?? "eng",
354
+ ocrEnabled: resolved.ocrEnabled ?? true,
355
+ ocrServerUrl: resolved.ocrServerUrl ?? void 0,
356
+ ocrServerHeaders: resolved.ocrServerHeaders ?? void 0,
357
+ tessdataPath: resolved.tessdataPath ?? void 0,
358
+ maxPages: resolved.maxPages ?? 1e3,
359
+ targetPages: resolved.targetPages ?? void 0,
360
+ extractScreenshots: resolved.extractScreenshots ?? false,
361
+ continueOnPageError: resolved.continueOnPageError ?? false,
362
+ dpi: resolved.dpi ?? 150,
363
+ outputFormat: resolved.outputFormat ?? "json",
364
+ imageMode: resolved.imageMode ?? "placeholder",
365
+ extractImages: resolved.extractImages ?? false,
366
+ imageOutputDir: resolved.imageOutputDir ?? void 0,
367
+ extractLinks: resolved.extractLinks ?? true,
368
+ keepHeadersFooters: resolved.keepHeadersFooters ?? false,
369
+ extractAnnotations: resolved.extractAnnotations ?? false,
370
+ extractFormFields: resolved.extractFormFields ?? false,
371
+ extractStructureTree: resolved.extractStructureTree ?? false,
372
+ extractBlocks: resolved.extractBlocks ?? false,
373
+ extractXfaPackets: resolved.extractXfaPackets ?? false,
374
+ extractDocumentMetadata: resolved.extractDocumentMetadata ?? false,
375
+ extractContentBounds: resolved.extractContentBounds ?? false,
376
+ detectScreenshotRects: resolved.detectScreenshotRects ?? false,
377
+ renderFormFields: resolved.renderFormFields ?? false,
378
+ preserveVerySmallText: resolved.preserveVerySmallText ?? false,
379
+ password: resolved.password ?? void 0,
380
+ quiet: resolved.quiet ?? false,
381
+ numWorkers: resolved.numWorkers ?? 1,
382
+ ocrFailureFatal: resolved.ocrFailureFatal ?? true,
383
+ ocrHedgeDelaysMs: resolved.ocrHedgeDelaysMs ?? [],
384
+ emitWordBoxes: resolved.emitWordBoxes ?? false,
385
+ extractTextMetadata: resolved.extractTextMetadata ?? false,
386
+ cropBox: resolved.cropBox ?? void 0,
387
+ skipDiagonalText: resolved.skipDiagonalText ?? false,
388
+ includeComplexity: resolved.includeComplexity ?? false,
389
+ extractVectorGraphics: resolved.extractVectorGraphics ?? false
212
390
  };
391
+ }
392
+ async parse(input) {
393
+ const nativeInput = typeof input === "string" ? input : Buffer.from(input);
394
+ if (this._pool !== null) {
395
+ const source = typeof nativeInput === "string" ? nativeInput : `<${nativeInput.byteLength} bytes>`;
396
+ return this._pool.parse(nativeInput, source);
397
+ }
398
+ const result = await this._native.parse(nativeInput);
399
+ return toParseResult(result);
400
+ }
401
+ /**
402
+ * Resolves once every pool worker is initialized. No-op without `poolSize`.
403
+ *
404
+ * Optional: the first parse on each worker waits for its init anyway. Call
405
+ * this before latency-sensitive traffic to avoid paying worker startup on
406
+ * the first request.
407
+ */
408
+ async warmUp() {
409
+ if (this._pool !== null) await this._pool.warmUp();
410
+ }
411
+ /**
412
+ * Shut down pool workers, if pool mode is enabled. Idempotent.
413
+ *
414
+ * Without `poolSize` this is a no-op. An idle pool never keeps the event
415
+ * loop alive and workers exit when the parent does, so forgetting to call
416
+ * this leaks nothing past process exit.
417
+ */
418
+ close() {
419
+ if (this._pool !== null) this._pool.close();
420
+ }
421
+ /**
422
+ * Parse a document in bounded-memory page batches of `batchSize` pages.
423
+ *
424
+ * Each yielded result is independent and becomes collectible once the caller
425
+ * advances the iterator, so a consumer that does not retain batches never
426
+ * holds more than one batch of pages in memory. A non-PDF source is
427
+ * converted once when the iterator starts, not once per batch; its temporary
428
+ * file is released when iteration ends — including an early `break` or
429
+ * `throw`, which run the generator's cleanup.
430
+ *
431
+ * Cross-page passes see only the pages in their own batch, so repeated
432
+ * header/footer removal and image deduplication are batch-local and the
433
+ * output can differ from `parse()`. Prefer `parse()` unless the size of the
434
+ * materialized result is the problem.
435
+ *
436
+ * As with any async generator, work starts on the first `next()` call, so
437
+ * errors (an unreadable file, or a parser configured with `targetPages` —
438
+ * ambiguous with generated batch ranges) surface on the first iteration
439
+ * rather than when `parseBatches()` itself is called.
440
+ */
441
+ async *parseBatches(input, options = {}) {
442
+ const nativeInput = typeof input === "string" ? input : Buffer.from(input);
443
+ const session = await this._native.openBatchSession(
444
+ nativeInput,
445
+ options.batchSize
446
+ );
447
+ try {
448
+ const totalPages = session.totalPages;
449
+ for (; ; ) {
450
+ const batch = await session.nextBatch();
451
+ if (batch == null) {
452
+ return;
453
+ }
454
+ yield {
455
+ startPage: batch.startPage,
456
+ endPage: batch.endPage,
457
+ totalPages,
458
+ result: toParseResult(batch.result)
459
+ };
460
+ }
461
+ } finally {
462
+ await session.close();
463
+ }
464
+ }
465
+ /**
466
+ * Parse from pre-extracted pages, skipping PDFium text extraction. Runs only
467
+ * grid projection + the configured output formatter, so the caller's own
468
+ * text-extraction / font-recovery owns the text content. Synchronous: no
469
+ * PDFium load and no OCR on this path.
470
+ */
471
+ parsePages(pages) {
472
+ const nativePages = pages.map((p) => ({
473
+ pageNumber: p.pageNumber,
474
+ pageWidth: p.pageWidth,
475
+ pageHeight: p.pageHeight,
476
+ textItems: p.textItems,
477
+ graphics: p.graphics
478
+ }));
479
+ const result = this._native.parsePages(nativePages);
480
+ return toParseResult(result);
481
+ }
482
+ /**
483
+ * Determine per-page complexity without running a full parse. Returns one
484
+ * entry per page with signals and a `needsOcr` verdict — a cheap pre-OCR
485
+ * check to decide whether a document needs advanced parsing.
486
+ */
487
+ async isComplex(input) {
488
+ const nativeInput = typeof input === "string" ? input : Buffer.from(input);
489
+ const stats = await this._native.isComplex(nativeInput);
490
+ return stats.map(toComplexity);
491
+ }
492
+ async screenshot(input, pageNumbers) {
493
+ const nativeInput = typeof input === "string" ? input : Buffer.from(input);
494
+ const results = await this._native.screenshot(
495
+ nativeInput,
496
+ pageNumbers ?? null
497
+ );
498
+ return results.map((r) => ({
499
+ pageNum: r.pageNum,
500
+ width: r.width,
501
+ height: r.height,
502
+ imageBuffer: r.imageBuffer,
503
+ isSolidFill: r.isSolidFill,
504
+ rects: r.rects
505
+ }));
506
+ }
507
+ getConfig() {
508
+ return { ...this._config };
509
+ }
510
+ };
511
+ function toComplexity(s) {
512
+ return {
513
+ pageNumber: s.pageNumber,
514
+ textLength: s.textLength,
515
+ textCoverage: s.textCoverage,
516
+ hasSubstantialImages: s.hasSubstantialImages,
517
+ imageBlockCount: s.imageBlockCount,
518
+ imageCoverage: s.imageCoverage,
519
+ largestImageCoverage: s.largestImageCoverage,
520
+ fullPageImage: s.fullPageImage,
521
+ uncoveredVectorArea: s.uncoveredVectorArea ?? void 0,
522
+ isGarbled: s.isGarbled,
523
+ pageArea: s.pageArea,
524
+ needsOcr: s.needsOcr,
525
+ reasons: s.reasons,
526
+ layout: s.layout ? {
527
+ columnCount: s.layout.columnCount,
528
+ ruledTableCount: s.layout.ruledTableCount,
529
+ ruledTableCoverage: s.layout.ruledTableCoverage,
530
+ textTableRunCount: s.layout.textTableRunCount,
531
+ figureCount: s.layout.figureCount,
532
+ figureCoverage: s.layout.figureCoverage,
533
+ isComplex: s.layout.isComplex,
534
+ reasons: s.layout.reasons
535
+ } : void 0
536
+ };
213
537
  }
214
538
  function toParseResult(result) {
215
- return {
216
- totalPages: result.totalPages,
217
- pages: result.pages.map(toPage),
218
- pageErrors: result.pageErrors ?? [],
219
- text: result.text,
220
- images: (result.images ?? []).map(toImage),
221
- screenshots: (result.screenshots ?? []).map(toScreenshot),
222
- imageErrorCount: result.imageErrorCount ?? 0,
223
- formType: result.formType,
224
- creator: result.creator,
225
- producer: result.producer,
226
- docMeta: result.docMeta,
227
- xfaPackets: result.xfaPackets,
228
- };
539
+ return {
540
+ totalPages: result.totalPages,
541
+ pages: result.pages.map(toPage),
542
+ pageErrors: result.pageErrors ?? [],
543
+ text: result.text,
544
+ images: (result.images ?? []).map(toImage),
545
+ screenshots: (result.screenshots ?? []).map(toScreenshot),
546
+ imageErrorCount: result.imageErrorCount ?? 0,
547
+ formType: result.formType,
548
+ creator: result.creator,
549
+ producer: result.producer,
550
+ docMeta: result.docMeta,
551
+ xfaPackets: result.xfaPackets
552
+ };
229
553
  }
230
554
  function toPage(p) {
231
- return {
232
- pageNum: p.pageNum,
233
- width: p.width,
234
- height: p.height,
235
- contentBounds: p.contentBounds,
236
- text: p.text,
237
- markdown: p.markdown,
238
- textItems: p.textItems.map(toTextItem),
239
- complexity: p.complexity ? toComplexity(p.complexity) : undefined,
240
- vectorGraphics: p.vectorGraphics ?? undefined,
241
- annotations: p.annotations,
242
- formFields: p.formFields?.map((field) => ({
243
- id: field.id,
244
- type: field.fieldType,
245
- page: field.page,
246
- annotationIndex: field.annotationIndex,
247
- widgetIndex: field.widgetIndex,
248
- objectNumber: field.objectNumber,
249
- name: field.name,
250
- alternateName: field.alternateName,
251
- value: field.value,
252
- exportValue: field.exportValue,
253
- fieldFlags: field.fieldFlags,
254
- controlCount: field.controlCount,
255
- controlIndex: field.controlIndex,
256
- checked: field.checked,
257
- rect: field.rect,
258
- options: field.options,
259
- selectedOptions: field.selectedOptions,
260
- })),
261
- structureTree: p.structureTree
262
- ? { roots: p.structureTree.roots.map(toStructureTreeElement) }
263
- : undefined,
264
- blocks: p.blocks,
265
- };
555
+ return {
556
+ pageNum: p.pageNum,
557
+ width: p.width,
558
+ height: p.height,
559
+ contentBounds: p.contentBounds,
560
+ text: p.text,
561
+ markdown: p.markdown,
562
+ textItems: p.textItems.map(toTextItem),
563
+ complexity: p.complexity ? toComplexity(p.complexity) : void 0,
564
+ vectorGraphics: p.vectorGraphics ?? void 0,
565
+ annotations: p.annotations,
566
+ formFields: p.formFields?.map((field) => ({
567
+ id: field.id,
568
+ type: field.fieldType,
569
+ page: field.page,
570
+ annotationIndex: field.annotationIndex,
571
+ widgetIndex: field.widgetIndex,
572
+ objectNumber: field.objectNumber,
573
+ name: field.name,
574
+ alternateName: field.alternateName,
575
+ value: field.value,
576
+ exportValue: field.exportValue,
577
+ fieldFlags: field.fieldFlags,
578
+ controlCount: field.controlCount,
579
+ controlIndex: field.controlIndex,
580
+ checked: field.checked,
581
+ rect: field.rect,
582
+ options: field.options,
583
+ selectedOptions: field.selectedOptions
584
+ })),
585
+ structureTree: p.structureTree ? { roots: p.structureTree.roots.map(toStructureTreeElement) } : void 0,
586
+ blocks: p.blocks
587
+ };
266
588
  }
267
589
  function toStructureTreeElement(element) {
268
- const attributes = {};
269
- for (const attribute of element.attributes) {
270
- if (attribute.booleanValue !== undefined) {
271
- attributes[attribute.name] = attribute.booleanValue;
272
- }
273
- else if (attribute.numberValue !== undefined) {
274
- attributes[attribute.name] = attribute.numberValue;
275
- }
276
- else if (attribute.stringValue !== undefined) {
277
- attributes[attribute.name] = attribute.stringValue;
278
- }
590
+ const attributes = {};
591
+ for (const attribute of element.attributes) {
592
+ if (attribute.booleanValue !== void 0) {
593
+ attributes[attribute.name] = attribute.booleanValue;
594
+ } else if (attribute.numberValue !== void 0) {
595
+ attributes[attribute.name] = attribute.numberValue;
596
+ } else if (attribute.stringValue !== void 0) {
597
+ attributes[attribute.name] = attribute.stringValue;
279
598
  }
280
- return {
281
- type: element.elementType,
282
- id: element.id,
283
- actualText: element.actualText,
284
- altText: element.altText,
285
- title: element.title,
286
- attributes,
287
- markedContentIds: element.markedContentIds,
288
- children: element.children.map(toStructureTreeElement),
289
- annotations: element.annotations,
290
- };
599
+ }
600
+ return {
601
+ type: element.elementType,
602
+ id: element.id,
603
+ actualText: element.actualText,
604
+ altText: element.altText,
605
+ title: element.title,
606
+ attributes,
607
+ markedContentIds: element.markedContentIds,
608
+ children: element.children.map(toStructureTreeElement),
609
+ annotations: element.annotations
610
+ };
291
611
  }
292
612
  function toImage(img) {
293
- return {
294
- id: img.id,
295
- name: img.name,
296
- path: img.path,
297
- page: img.page,
298
- bbox: img.bbox,
299
- width: img.width,
300
- height: img.height,
301
- rotation: img.rotation,
302
- format: img.format,
303
- duplicateOf: img.duplicateOf,
304
- bytes: img.bytes,
305
- };
613
+ return {
614
+ id: img.id,
615
+ name: img.name,
616
+ path: img.path,
617
+ page: img.page,
618
+ bbox: img.bbox,
619
+ width: img.width,
620
+ height: img.height,
621
+ rotation: img.rotation,
622
+ format: img.format,
623
+ duplicateOf: img.duplicateOf,
624
+ bytes: img.bytes
625
+ };
306
626
  }
307
627
  function toScreenshot(result) {
308
- return {
309
- pageNum: result.pageNum,
310
- width: result.width,
311
- height: result.height,
312
- imageBuffer: result.imageBuffer,
313
- isSolidFill: result.isSolidFill,
314
- rects: result.rects,
315
- };
628
+ return {
629
+ pageNum: result.pageNum,
630
+ width: result.width,
631
+ height: result.height,
632
+ imageBuffer: result.imageBuffer,
633
+ isSolidFill: result.isSolidFill,
634
+ rects: result.rects
635
+ };
316
636
  }
317
637
  function toTextItem(item) {
318
- return {
319
- text: item.text,
320
- x: item.x,
321
- y: item.y,
322
- width: item.width,
323
- height: item.height,
324
- fontName: item.fontName,
325
- fontSize: item.fontSize,
326
- fontHeight: item.fontHeight,
327
- fontAscent: item.fontAscent,
328
- fontDescent: item.fontDescent,
329
- fontWeight: item.fontWeight,
330
- textWidth: item.textWidth,
331
- fontIsBuggy: item.fontIsBuggy,
332
- mcid: item.mcid,
333
- fillColor: item.fillColor,
334
- strokeColor: item.strokeColor,
335
- charCodes: item.charCodes,
336
- trailingSpaceGenerated: item.trailingSpaceGenerated,
337
- confidence: item.confidence,
338
- rotation: item.rotation,
339
- words: item.words,
340
- };
638
+ return {
639
+ text: item.text,
640
+ x: item.x,
641
+ y: item.y,
642
+ width: item.width,
643
+ height: item.height,
644
+ fontName: item.fontName,
645
+ fontSize: item.fontSize,
646
+ fontHeight: item.fontHeight,
647
+ fontAscent: item.fontAscent,
648
+ fontDescent: item.fontDescent,
649
+ fontWeight: item.fontWeight,
650
+ textWidth: item.textWidth,
651
+ fontIsBuggy: item.fontIsBuggy,
652
+ mcid: item.mcid,
653
+ fillColor: item.fillColor,
654
+ strokeColor: item.strokeColor,
655
+ charCodes: item.charCodes,
656
+ trailingSpaceGenerated: item.trailingSpaceGenerated,
657
+ confidence: item.confidence,
658
+ rotation: item.rotation,
659
+ words: item.words
660
+ };
341
661
  }
342
- export function searchItems(items, options) {
343
- const nativeResults = native.searchItems(items, options.phrase, options.caseSensitive ?? false);
344
- return nativeResults.map(toTextItem);
662
+ function searchItems(items, options) {
663
+ const nativeResults = native.searchItems(
664
+ items,
665
+ options.phrase,
666
+ options.caseSensitive ?? false
667
+ );
668
+ return nativeResults.map(toTextItem);
345
669
  }
346
- export default LiteParse;
670
+ var lib_default = LiteParse;
671
+ export {
672
+ LiteParse,
673
+ ParseTimeoutError,
674
+ lib_default as default,
675
+ searchItems,
676
+ toParseResult
677
+ };
347
678
  //# sourceMappingURL=lib.js.map