@bendyline/squisq-formats 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CX3LH47J.js → chunk-2KPARF2P.js} +3 -3
- package/dist/{chunk-CVNVD7VH.js → chunk-ABVI556T.js} +5 -5
- package/dist/{chunk-2CSZJEAL.js → chunk-CRAVSMPZ.js} +3 -3
- package/dist/{chunk-H7T5DXUU.js → chunk-FE6OJV6O.js} +95 -14
- package/dist/chunk-FE6OJV6O.js.map +1 -0
- package/dist/{chunk-2EFDWKC6.js → chunk-HTW2M27H.js} +86 -23
- package/dist/chunk-HTW2M27H.js.map +1 -0
- package/dist/{chunk-ZZ7D4DN6.js → chunk-QRVN6A6E.js} +39 -9
- package/dist/chunk-QRVN6A6E.js.map +1 -0
- package/dist/{chunk-XTLQXR4U.js → chunk-U32AG3G3.js} +3 -3
- package/dist/{chunk-HBSCRS5P.js → chunk-VJJM2SSH.js} +3 -3
- package/dist/{chunk-2PRVLAU3.js → chunk-VSYHZECT.js} +55 -17
- package/dist/chunk-VSYHZECT.js.map +1 -0
- package/dist/{chunk-4HJ6AK2Q.js → chunk-WC7WULGV.js} +44 -13
- package/dist/chunk-WC7WULGV.js.map +1 -0
- package/dist/{chunk-X4USWOQ2.js → chunk-XKUMNGBW.js} +2 -2
- package/dist/container/index.d.ts +10 -4
- package/dist/container/index.js +9 -4
- package/dist/container/index.js.map +1 -1
- package/dist/docx/index.d.ts +2 -2
- package/dist/docx/index.js +4 -4
- package/dist/{export-Djev9BLU.d.ts → export-D2NkylDT.d.ts} +1 -1
- package/dist/{extract-LNOA7DBK.js → extract-H6RXJMHP.js} +5 -5
- package/dist/{import-Yivc3kZM.d.ts → import-K8mfc0fz.d.ts} +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +14 -12
- package/dist/infer/index.d.ts +4 -4
- package/dist/infer/index.js +5 -5
- package/dist/{layouts-CZ0bdsNL.d.ts → layouts-BHrgZ5FS.d.ts} +1 -1
- package/dist/{layouts-7OUUVAGU.js → layouts-QVPK3ZCU.js} +4 -4
- package/dist/ooxml/index.d.ts +4 -4
- package/dist/ooxml/index.js +4 -4
- package/dist/pptx/index.d.ts +5 -5
- package/dist/pptx/index.js +5 -5
- package/dist/{reader-Gof6OrJX.d.ts → reader-B9L8Ucbj.d.ts} +3 -3
- package/dist/registry/index.d.ts +20 -6
- package/dist/registry/index.js +6 -4
- package/dist/{themeReader-DPw4J5Ss.d.ts → themeReader-DJKErl_j.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +4 -4
- package/dist/xlsx/index.js +4 -4
- package/dist/{zipLimits-bGYMS9cI.d.ts → zipLimits-BOKCB7qk.d.ts} +2 -0
- package/package.json +2 -2
- package/src/__tests__/convert.test.ts +143 -1
- package/src/__tests__/ooxml.test.ts +17 -0
- package/src/__tests__/ooxmlCancellation.test.ts +113 -0
- package/src/container/index.ts +17 -3
- package/src/index.ts +3 -0
- package/src/infer/index.ts +35 -4
- package/src/ooxml/reader.ts +55 -14
- package/src/pptx/layouts.ts +68 -14
- package/src/registry/convert.ts +115 -31
- package/src/registry/defaultFormats.ts +5 -2
- package/src/registry/index.ts +3 -1
- package/src/registry/types.ts +18 -1
- package/src/shared/boundedZipArchive.ts +49 -8
- package/src/shared/zipEntryCount.ts +59 -5
- package/src/shared/zipLimits.ts +10 -0
- package/dist/chunk-2EFDWKC6.js.map +0 -1
- package/dist/chunk-2PRVLAU3.js.map +0 -1
- package/dist/chunk-4HJ6AK2Q.js.map +0 -1
- package/dist/chunk-H7T5DXUU.js.map +0 -1
- package/dist/chunk-ZZ7D4DN6.js.map +0 -1
- /package/dist/{chunk-CX3LH47J.js.map → chunk-2KPARF2P.js.map} +0 -0
- /package/dist/{chunk-CVNVD7VH.js.map → chunk-ABVI556T.js.map} +0 -0
- /package/dist/{chunk-2CSZJEAL.js.map → chunk-CRAVSMPZ.js.map} +0 -0
- /package/dist/{chunk-XTLQXR4U.js.map → chunk-U32AG3G3.js.map} +0 -0
- /package/dist/{chunk-HBSCRS5P.js.map → chunk-VJJM2SSH.js.map} +0 -0
- /package/dist/{chunk-X4USWOQ2.js.map → chunk-XKUMNGBW.js.map} +0 -0
- /package/dist/{extract-LNOA7DBK.js.map → extract-H6RXJMHP.js.map} +0 -0
- /package/dist/{layouts-7OUUVAGU.js.map → layouts-QVPK3ZCU.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseThemeXml,
|
|
3
3
|
readThemePart
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XKUMNGBW.js";
|
|
5
5
|
import {
|
|
6
6
|
findRelByType,
|
|
7
7
|
resolveTarget
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
REL_SLIDE_MASTER,
|
|
14
14
|
getPartRelationships,
|
|
15
15
|
getPartXml
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WC7WULGV.js";
|
|
17
17
|
|
|
18
18
|
// src/infer/extract.ts
|
|
19
19
|
var DEFAULT_COLOR_MAP = { bg1: "lt1", tx1: "dk1" };
|
|
@@ -96,4 +96,4 @@ export {
|
|
|
96
96
|
extractPptxTheme,
|
|
97
97
|
extractXlsxTheme
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-2KPARF2P.js.map
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-YRT7GQ5Y.js";
|
|
14
14
|
import {
|
|
15
15
|
createPackage
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-U32AG3G3.js";
|
|
17
17
|
import {
|
|
18
18
|
escapeXml,
|
|
19
19
|
xmlDeclaration
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
getPartRelationships,
|
|
41
41
|
getPartXml,
|
|
42
42
|
openPackage
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-WC7WULGV.js";
|
|
44
44
|
import {
|
|
45
45
|
buildContainer
|
|
46
46
|
} from "./chunk-SSUPBUF5.js";
|
|
@@ -959,7 +959,7 @@ async function importDocument(pkg, options) {
|
|
|
959
959
|
let extraction = null;
|
|
960
960
|
if (inferTheme || inferLayouts) {
|
|
961
961
|
try {
|
|
962
|
-
const { extractPptxTheme } = await import("./extract-
|
|
962
|
+
const { extractPptxTheme } = await import("./extract-H6RXJMHP.js");
|
|
963
963
|
extraction = await extractPptxTheme(pkg);
|
|
964
964
|
} catch (err) {
|
|
965
965
|
warnInferenceFailure("theme extraction", err);
|
|
@@ -976,7 +976,7 @@ async function importDocument(pkg, options) {
|
|
|
976
976
|
}
|
|
977
977
|
if (inferLayouts) {
|
|
978
978
|
try {
|
|
979
|
-
const { analyzePptxLayouts: analyzePptxLayouts2 } = await import("./layouts-
|
|
979
|
+
const { analyzePptxLayouts: analyzePptxLayouts2 } = await import("./layouts-QVPK3ZCU.js");
|
|
980
980
|
const { colorHintsFromExtraction } = await import("./mapTheme-IR27S6IV.js");
|
|
981
981
|
ctx.inference = await analyzePptxLayouts2(pkg, {
|
|
982
982
|
colors: extraction ? colorHintsFromExtraction(extraction) : {}
|
|
@@ -1037,4 +1037,4 @@ export {
|
|
|
1037
1037
|
pptxToContainer,
|
|
1038
1038
|
pptxToDoc
|
|
1039
1039
|
};
|
|
1040
|
-
//# sourceMappingURL=chunk-
|
|
1040
|
+
//# sourceMappingURL=chunk-ABVI556T.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-PN52A5AA.js";
|
|
8
8
|
import {
|
|
9
9
|
createPackage
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-U32AG3G3.js";
|
|
11
11
|
import {
|
|
12
12
|
escapeXml,
|
|
13
13
|
xmlDeclaration
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getPartRelationships,
|
|
35
35
|
getPartXml,
|
|
36
36
|
openPackage
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-WC7WULGV.js";
|
|
38
38
|
import {
|
|
39
39
|
buildContainer
|
|
40
40
|
} from "./chunk-SSUPBUF5.js";
|
|
@@ -1209,4 +1209,4 @@ export {
|
|
|
1209
1209
|
docxToDoc,
|
|
1210
1210
|
docxToContainer
|
|
1211
1211
|
};
|
|
1212
|
-
//# sourceMappingURL=chunk-
|
|
1212
|
+
//# sourceMappingURL=chunk-CRAVSMPZ.js.map
|
|
@@ -16,12 +16,14 @@ var DEFAULT_MAX_ZIP_ENTRIES = 1e4;
|
|
|
16
16
|
var DEFAULT_MAX_ZIP_UNCOMPRESSED_BYTES = 512 * 1024 * 1024;
|
|
17
17
|
var DEFAULT_MAX_ZIP_COMPRESSION_RATIO = 1e3;
|
|
18
18
|
function validateZipArchive(zip, limits = {}) {
|
|
19
|
+
throwIfZipAborted(limits.signal);
|
|
19
20
|
const resolved = resolveZipSafetyLimits(limits);
|
|
20
21
|
const files = [];
|
|
21
22
|
let memberCount = 0;
|
|
22
23
|
let declaredTotal = 0;
|
|
23
24
|
let oversizedEntry;
|
|
24
25
|
zip.forEach((relativePath, entry) => {
|
|
26
|
+
throwIfZipAborted(limits.signal);
|
|
25
27
|
memberCount++;
|
|
26
28
|
if (entry.dir) return;
|
|
27
29
|
const originalPath = originalEntryPath(entry, relativePath);
|
|
@@ -65,6 +67,10 @@ function validateZipArchive(zip, limits = {}) {
|
|
|
65
67
|
}
|
|
66
68
|
return files;
|
|
67
69
|
}
|
|
70
|
+
function throwIfZipAborted(signal) {
|
|
71
|
+
if (!signal?.aborted) return;
|
|
72
|
+
throw signal.reason ?? new Error("ZIP operation was cancelled");
|
|
73
|
+
}
|
|
68
74
|
function assertSafeZipPath(path) {
|
|
69
75
|
if (!isSafeArchivePath(path)) {
|
|
70
76
|
throw new ZipSafetyError("unsafe-path", `ZIP archive path is unsafe: ${path}.`, { path });
|
|
@@ -181,7 +187,8 @@ var ZIP64_END_LOCATOR = 117853008;
|
|
|
181
187
|
var UINT16_MAX = 65535;
|
|
182
188
|
var MIN_EOCD_BYTES = 22;
|
|
183
189
|
var MAX_ZIP_COMMENT_BYTES = 65535;
|
|
184
|
-
async function prepareZipInput(data) {
|
|
190
|
+
async function prepareZipInput(data, signal) {
|
|
191
|
+
throwIfZipAborted(signal);
|
|
185
192
|
if (ArrayBuffer.isView(data)) {
|
|
186
193
|
const bytes2 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
187
194
|
return { input: bytes2, bytes: bytes2 };
|
|
@@ -190,7 +197,8 @@ async function prepareZipInput(data) {
|
|
|
190
197
|
const bytes2 = new Uint8Array(data);
|
|
191
198
|
return { input: bytes2, bytes: bytes2 };
|
|
192
199
|
}
|
|
193
|
-
const bytes = await readBlobBytes(data);
|
|
200
|
+
const bytes = await readBlobBytes(data, signal);
|
|
201
|
+
throwIfZipAborted(signal);
|
|
194
202
|
return bytes ? { input: bytes, bytes } : { input: data };
|
|
195
203
|
}
|
|
196
204
|
function declaredZipEntryCount(bytes) {
|
|
@@ -234,29 +242,73 @@ function isBlobLike(data) {
|
|
|
234
242
|
const candidate = data;
|
|
235
243
|
return typeof candidate.size === "number" && typeof candidate.slice === "function";
|
|
236
244
|
}
|
|
237
|
-
async function readBlobBytes(blob) {
|
|
245
|
+
async function readBlobBytes(blob, signal) {
|
|
238
246
|
const arrayBuffer = blob.arrayBuffer;
|
|
239
247
|
if (typeof arrayBuffer === "function") {
|
|
240
|
-
return new Uint8Array(await arrayBuffer.call(blob));
|
|
248
|
+
return new Uint8Array(await raceWithAbort(arrayBuffer.call(blob), signal));
|
|
241
249
|
}
|
|
242
250
|
if (typeof FileReader === "undefined") return void 0;
|
|
243
251
|
return new Promise((resolve, reject) => {
|
|
244
252
|
const reader = new FileReader();
|
|
245
|
-
|
|
246
|
-
|
|
253
|
+
let settled = false;
|
|
254
|
+
const cleanup = () => signal?.removeEventListener("abort", handleAbort);
|
|
255
|
+
const finish = (work) => {
|
|
256
|
+
if (settled) return;
|
|
257
|
+
settled = true;
|
|
258
|
+
cleanup();
|
|
259
|
+
work();
|
|
260
|
+
};
|
|
261
|
+
const handleAbort = () => {
|
|
262
|
+
if (settled) return;
|
|
263
|
+
const reason = signal?.reason ?? new Error("ZIP operation was cancelled");
|
|
264
|
+
finish(() => reject(reason));
|
|
265
|
+
try {
|
|
266
|
+
reader.abort();
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
reader.onload = () => finish(() => resolve(new Uint8Array(reader.result)));
|
|
271
|
+
reader.onerror = () => finish(() => reject(reader.error ?? new Error("Could not read ZIP blob.")));
|
|
272
|
+
reader.onabort = handleAbort;
|
|
273
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
274
|
+
if (signal?.aborted) {
|
|
275
|
+
handleAbort();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
247
278
|
reader.readAsArrayBuffer(blob);
|
|
248
279
|
});
|
|
249
280
|
}
|
|
281
|
+
function raceWithAbort(work, signal) {
|
|
282
|
+
throwIfZipAborted(signal);
|
|
283
|
+
if (!signal) return work;
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
let settled = false;
|
|
286
|
+
const finish = (complete) => {
|
|
287
|
+
if (settled) return;
|
|
288
|
+
settled = true;
|
|
289
|
+
signal.removeEventListener("abort", handleAbort);
|
|
290
|
+
complete();
|
|
291
|
+
};
|
|
292
|
+
const handleAbort = () => finish(() => reject(signal.reason ?? new Error("ZIP operation was cancelled")));
|
|
293
|
+
signal.addEventListener("abort", handleAbort, { once: true });
|
|
294
|
+
if (signal.aborted) handleAbort();
|
|
295
|
+
work.then(
|
|
296
|
+
(value) => finish(() => resolve(value)),
|
|
297
|
+
(error) => finish(() => reject(error))
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
250
301
|
|
|
251
302
|
// src/shared/boundedZipArchive.ts
|
|
252
303
|
var JsZipBoundedArchive = class {
|
|
253
|
-
constructor(zip, entries, limits) {
|
|
304
|
+
constructor(zip, entries, limits, signal) {
|
|
254
305
|
this.reads = /* @__PURE__ */ new Map();
|
|
255
306
|
this.activeReads = /* @__PURE__ */ new Set();
|
|
256
307
|
this.emittedBytes = 0;
|
|
257
308
|
this.zip = zip;
|
|
258
309
|
this.entries = entries;
|
|
259
310
|
this.limits = limits;
|
|
311
|
+
this.signal = signal;
|
|
260
312
|
this.entriesByPath = new Map(entries.map((entry) => [entry.path, entry]));
|
|
261
313
|
}
|
|
262
314
|
/** Actual uncompressed bytes accepted from JSZip streams so far. */
|
|
@@ -265,6 +317,7 @@ var JsZipBoundedArchive = class {
|
|
|
265
317
|
}
|
|
266
318
|
/** Read and cache one member, returning null when it does not exist. */
|
|
267
319
|
async read(path, maxBytes) {
|
|
320
|
+
throwIfZipAborted(this.signal);
|
|
268
321
|
this.throwIfFailed();
|
|
269
322
|
if (maxBytes !== void 0) assertNonNegativeFinite("member read limit", maxBytes);
|
|
270
323
|
const entry = this.entriesByPath.get(path);
|
|
@@ -274,7 +327,14 @@ var JsZipBoundedArchive = class {
|
|
|
274
327
|
pending = this.streamEntry(entry, maxBytes);
|
|
275
328
|
this.reads.set(path, pending);
|
|
276
329
|
}
|
|
277
|
-
|
|
330
|
+
let bytes;
|
|
331
|
+
try {
|
|
332
|
+
bytes = await pending;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (this.reads.get(path) === pending && !this.terminalError) this.reads.delete(path);
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
throwIfZipAborted(this.signal);
|
|
278
338
|
if (maxBytes !== void 0 && bytes.byteLength > maxBytes) {
|
|
279
339
|
const error = entryTooLargeError(path, bytes.byteLength, maxBytes);
|
|
280
340
|
this.failArchive(error);
|
|
@@ -292,6 +352,7 @@ var JsZipBoundedArchive = class {
|
|
|
292
352
|
this.reads.delete(path);
|
|
293
353
|
}
|
|
294
354
|
streamEntry(metadata, readLimit) {
|
|
355
|
+
throwIfZipAborted(this.signal);
|
|
295
356
|
const { path, entry, declaredSize, crc32: expectedCrc } = metadata;
|
|
296
357
|
const effectiveEntryLimit = Math.min(
|
|
297
358
|
this.limits.maxEntryUncompressedBytes,
|
|
@@ -310,11 +371,14 @@ var JsZipBoundedArchive = class {
|
|
|
310
371
|
let settled = false;
|
|
311
372
|
let written = 0;
|
|
312
373
|
let crc = 4294967295;
|
|
374
|
+
const removeAbortListener = () => this.signal?.removeEventListener("abort", handleAbort);
|
|
375
|
+
const handleAbort = () => controller.stop(this.signal?.reason ?? new Error("ZIP operation was cancelled"));
|
|
313
376
|
const controller = {
|
|
314
377
|
stop: (error) => {
|
|
315
378
|
if (settled) return;
|
|
316
379
|
settled = true;
|
|
317
380
|
this.activeReads.delete(controller);
|
|
381
|
+
removeAbortListener();
|
|
318
382
|
try {
|
|
319
383
|
stream.pause();
|
|
320
384
|
} catch {
|
|
@@ -323,17 +387,26 @@ var JsZipBoundedArchive = class {
|
|
|
323
387
|
}
|
|
324
388
|
};
|
|
325
389
|
this.activeReads.add(controller);
|
|
390
|
+
if (this.signal) {
|
|
391
|
+
this.signal.addEventListener("abort", handleAbort, { once: true });
|
|
392
|
+
if (this.signal.aborted) {
|
|
393
|
+
handleAbort();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
326
397
|
stream.on("data", (chunk) => {
|
|
327
398
|
if (settled) return;
|
|
328
399
|
const nextWritten = written + chunk.byteLength;
|
|
329
400
|
try {
|
|
401
|
+
throwIfZipAborted(this.signal);
|
|
330
402
|
this.assertChunkAllowed(metadata, nextWritten, chunk.byteLength, effectiveEntryLimit);
|
|
331
403
|
output = growOutput(output, nextWritten, declaredSize, effectiveEntryLimit);
|
|
332
404
|
output.set(chunk, written);
|
|
333
405
|
written = nextWritten;
|
|
334
406
|
crc = updateCrc32(crc, chunk);
|
|
335
407
|
} catch (error) {
|
|
336
|
-
this.
|
|
408
|
+
if (this.signal?.aborted) controller.stop(error);
|
|
409
|
+
else this.failArchive(asZipSafetyError(error, path));
|
|
337
410
|
}
|
|
338
411
|
}).on("error", (error) => {
|
|
339
412
|
if (settled) return;
|
|
@@ -368,6 +441,7 @@ var JsZipBoundedArchive = class {
|
|
|
368
441
|
}
|
|
369
442
|
settled = true;
|
|
370
443
|
this.activeReads.delete(controller);
|
|
444
|
+
removeAbortListener();
|
|
371
445
|
resolve(output.byteLength === written ? output : output.slice(0, written));
|
|
372
446
|
}).resume();
|
|
373
447
|
});
|
|
@@ -406,13 +480,16 @@ var JsZipBoundedArchive = class {
|
|
|
406
480
|
}
|
|
407
481
|
};
|
|
408
482
|
async function openBoundedZipArchive(data, limits = {}) {
|
|
483
|
+
throwIfZipAborted(limits.signal);
|
|
409
484
|
const resolved = resolveZipSafetyLimits(limits);
|
|
410
485
|
let prepared;
|
|
411
486
|
try {
|
|
412
|
-
prepared = await prepareZipInput(data);
|
|
487
|
+
prepared = await prepareZipInput(data, limits.signal);
|
|
413
488
|
} catch (error) {
|
|
489
|
+
if (limits.signal?.aborted) throw limits.signal.reason ?? error;
|
|
414
490
|
throw invalidArchiveError(error);
|
|
415
491
|
}
|
|
492
|
+
throwIfZipAborted(limits.signal);
|
|
416
493
|
if (prepared.bytes) {
|
|
417
494
|
const declaredEntries = declaredZipEntryCount(prepared.bytes);
|
|
418
495
|
if (declaredEntries !== void 0 && declaredEntries > resolved.maxEntries) {
|
|
@@ -423,17 +500,21 @@ async function openBoundedZipArchive(data, limits = {}) {
|
|
|
423
500
|
try {
|
|
424
501
|
zip = await JSZip.loadAsync(prepared.input);
|
|
425
502
|
} catch (error) {
|
|
503
|
+
if (limits.signal?.aborted) throw limits.signal.reason ?? error;
|
|
426
504
|
throw invalidArchiveError(error);
|
|
427
505
|
}
|
|
428
|
-
|
|
506
|
+
throwIfZipAborted(limits.signal);
|
|
507
|
+
return createBoundedZipArchive(zip, limits);
|
|
429
508
|
}
|
|
430
509
|
function invalidArchiveError(cause) {
|
|
431
510
|
return new ZipSafetyError("invalid-archive", "ZIP import: archive is not readable.", { cause });
|
|
432
511
|
}
|
|
433
512
|
function createBoundedZipArchive(zip, limits = {}) {
|
|
513
|
+
throwIfZipAborted(limits.signal);
|
|
434
514
|
const resolved = resolveZipSafetyLimits(limits);
|
|
435
|
-
const entries = validateZipArchive(zip,
|
|
436
|
-
|
|
515
|
+
const entries = validateZipArchive(zip, limits);
|
|
516
|
+
throwIfZipAborted(limits.signal);
|
|
517
|
+
return new JsZipBoundedArchive(zip, entries, resolved, limits.signal);
|
|
437
518
|
}
|
|
438
519
|
function asZipSafetyError(error, path) {
|
|
439
520
|
if (error instanceof ZipSafetyError) return error;
|
|
@@ -476,4 +557,4 @@ export {
|
|
|
476
557
|
assertSafeZipPath,
|
|
477
558
|
openBoundedZipArchive
|
|
478
559
|
};
|
|
479
|
-
//# sourceMappingURL=chunk-
|
|
560
|
+
//# sourceMappingURL=chunk-FE6OJV6O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/zipLimits.ts","../src/shared/boundedZipArchive.ts","../src/shared/zipEntryCount.ts"],"sourcesContent":["/** ZIP safety policy, structured errors, path checks, and metadata validation. */\n\nimport type JSZip from 'jszip';\n\nexport interface ZipSafetyLimits {\n /** Cooperatively cancel archive opening, validation, and member traversal. */\n signal?: AbortSignal;\n /** Maximum number of archive records, including directories. Default: 10,000. */\n maxEntries?: number;\n /** Maximum uncompressed bytes for one member. Defaults to maxUncompressedBytes. */\n maxEntryUncompressedBytes?: number;\n /** Maximum total uncompressed bytes across the archive. Default: 512 MiB. */\n maxUncompressedBytes?: number;\n /** Maximum uncompressed/compressed ratio for a member. Default: 1,000. */\n maxCompressionRatio?: number;\n}\n\nexport type ZipSafetyErrorCode =\n | 'invalid-limit'\n | 'invalid-archive'\n | 'unsafe-path'\n | 'invalid-entry-metadata'\n | 'too-many-entries'\n | 'entry-too-large'\n | 'archive-too-large'\n | 'compression-ratio-exceeded'\n | 'size-mismatch'\n | 'crc-mismatch'\n | 'decompression-failed';\n\nexport interface ZipSafetyErrorOptions {\n path?: string;\n limit?: number;\n actual?: number;\n cause?: unknown;\n}\n\n/** A stable, machine-readable error raised while validating or reading a ZIP. */\nexport class ZipSafetyError extends Error {\n readonly code: ZipSafetyErrorCode;\n readonly path?: string;\n readonly limit?: number;\n readonly actual?: number;\n\n constructor(code: ZipSafetyErrorCode, message: string, options: ZipSafetyErrorOptions = {}) {\n super(message);\n this.name = 'ZipSafetyError';\n this.code = code;\n this.path = options.path;\n this.limit = options.limit;\n this.actual = options.actual;\n if (options.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = options.cause;\n }\n }\n}\n\nexport interface ValidatedZipEntry {\n path: string;\n entry: JSZip.JSZipObject;\n declaredSize?: number;\n compressedSize?: number;\n crc32?: number;\n}\n\nexport interface ResolvedZipSafetyLimits {\n maxEntries: number;\n maxEntryUncompressedBytes: number;\n maxUncompressedBytes: number;\n maxCompressionRatio: number;\n}\n\nexport const DEFAULT_MAX_ZIP_ENTRIES = 10_000;\nexport const DEFAULT_MAX_ZIP_UNCOMPRESSED_BYTES = 512 * 1024 * 1024;\nexport const DEFAULT_MAX_ZIP_COMPRESSION_RATIO = 1_000;\n\n/**\n * Validate central-directory metadata before any member is decompressed.\n * JSZip normalizes traversal names, so inspect `unsafeOriginalName` too.\n */\nexport function validateZipArchive(zip: JSZip, limits: ZipSafetyLimits = {}): ValidatedZipEntry[] {\n throwIfZipAborted(limits.signal);\n const resolved = resolveZipSafetyLimits(limits);\n const files: ValidatedZipEntry[] = [];\n let memberCount = 0;\n let declaredTotal = 0;\n let oversizedEntry: { path: string; actual: number; limit: number } | undefined;\n\n zip.forEach((relativePath, entry) => {\n throwIfZipAborted(limits.signal);\n memberCount++;\n if (entry.dir) return;\n const originalPath = originalEntryPath(entry, relativePath);\n if (!isSafeArchivePath(originalPath)) {\n throw new ZipSafetyError(\n 'unsafe-path',\n `ZIP import: archive contains an unsafe path: ${originalPath}.`,\n { path: originalPath },\n );\n }\n\n const metadata = compressedMetadata(entry, originalPath);\n if (\n metadata.uncompressedSize !== undefined &&\n metadata.uncompressedSize > resolved.maxEntryUncompressedBytes\n ) {\n oversizedEntry ??= {\n path: originalPath,\n actual: metadata.uncompressedSize,\n limit: resolved.maxEntryUncompressedBytes,\n };\n }\n\n const ratio =\n metadata.uncompressedSize === undefined || metadata.compressedSize === undefined\n ? undefined\n : compressionRatio(metadata.uncompressedSize, metadata.compressedSize);\n if (ratio !== undefined && ratio > resolved.maxCompressionRatio) {\n throw compressionRatioError(originalPath, ratio, resolved.maxCompressionRatio);\n }\n\n declaredTotal += metadata.uncompressedSize ?? 0;\n if (!Number.isSafeInteger(declaredTotal)) throw invalidMetadataError(originalPath);\n files.push({\n path: relativePath,\n entry,\n ...(metadata.uncompressedSize !== undefined\n ? { declaredSize: metadata.uncompressedSize }\n : {}),\n ...(metadata.compressedSize !== undefined ? { compressedSize: metadata.compressedSize } : {}),\n ...(metadata.crc32 !== undefined ? { crc32: metadata.crc32 } : {}),\n });\n });\n\n if (memberCount > resolved.maxEntries) {\n throw tooManyEntriesError(memberCount, resolved.maxEntries);\n }\n if (declaredTotal > resolved.maxUncompressedBytes) {\n throw archiveTooLargeError(declaredTotal, resolved.maxUncompressedBytes);\n }\n if (oversizedEntry) {\n throw entryTooLargeError(oversizedEntry.path, oversizedEntry.actual, oversizedEntry.limit);\n }\n return files;\n}\n\n/** Preserve the caller's exact cancellation reason across ZIP/OOXML layers. */\nexport function throwIfZipAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n throw signal.reason ?? new Error('ZIP operation was cancelled');\n}\n\n/** Reject a path that could be interpreted outside an archive's logical root. */\nexport function assertSafeZipPath(path: string): void {\n if (!isSafeArchivePath(path)) {\n throw new ZipSafetyError('unsafe-path', `ZIP archive path is unsafe: ${path}.`, { path });\n }\n}\n\nexport function resolveZipSafetyLimits(limits: ZipSafetyLimits): ResolvedZipSafetyLimits {\n const maxEntries = limits.maxEntries ?? DEFAULT_MAX_ZIP_ENTRIES;\n const maxUncompressedBytes = limits.maxUncompressedBytes ?? DEFAULT_MAX_ZIP_UNCOMPRESSED_BYTES;\n const maxEntryUncompressedBytes = limits.maxEntryUncompressedBytes ?? maxUncompressedBytes;\n const maxCompressionRatio = limits.maxCompressionRatio ?? DEFAULT_MAX_ZIP_COMPRESSION_RATIO;\n\n assertNonNegativeInteger('maxEntries', maxEntries);\n assertNonNegativeFinite('maxUncompressedBytes', maxUncompressedBytes);\n assertNonNegativeFinite('maxEntryUncompressedBytes', maxEntryUncompressedBytes);\n if (!Number.isFinite(maxCompressionRatio) || maxCompressionRatio < 1) {\n throw invalidLimitError(\n 'maxCompressionRatio must be a finite number greater than or equal to 1',\n );\n }\n return {\n maxEntries,\n maxEntryUncompressedBytes,\n maxUncompressedBytes,\n maxCompressionRatio,\n };\n}\n\nexport function assertNonNegativeFinite(name: string, value: number): void {\n if (!Number.isFinite(value) || value < 0) {\n throw invalidLimitError(`${name} must be a non-negative finite number`);\n }\n}\n\nexport function entryTooLargeError(path: string, actual: number, limit: number): ZipSafetyError {\n return new ZipSafetyError(\n 'entry-too-large',\n `ZIP import: archive member ${path} exceeds ${limit} byte per-entry limit.`,\n { path, limit, actual },\n );\n}\n\nexport function tooManyEntriesError(actual: number, limit: number): ZipSafetyError {\n return new ZipSafetyError(\n 'too-many-entries',\n `ZIP import: archive has ${actual} files; limit is ${limit}.`,\n { limit, actual },\n );\n}\n\nexport function archiveTooLargeError(actual: number, limit: number): ZipSafetyError {\n return new ZipSafetyError(\n 'archive-too-large',\n `ZIP import: uncompressed content exceeds ${limit} byte limit.`,\n { limit, actual },\n );\n}\n\nexport function compressionRatioError(path: string, actual: number, limit: number): ZipSafetyError {\n return new ZipSafetyError(\n 'compression-ratio-exceeded',\n `ZIP import: archive member ${path} exceeds ${limit}:1 compression-ratio limit.`,\n { path, limit, actual },\n );\n}\n\nexport function compressionRatio(uncompressedBytes: number, compressedBytes: number): number {\n if (uncompressedBytes === 0) return 0;\n if (compressedBytes === 0) return Number.POSITIVE_INFINITY;\n return uncompressedBytes / compressedBytes;\n}\n\nfunction assertNonNegativeInteger(name: string, value: number): void {\n if (!Number.isInteger(value) || value < 0) {\n throw invalidLimitError(`${name} must be a non-negative integer`);\n }\n}\n\nfunction invalidLimitError(detail: string): ZipSafetyError {\n return new ZipSafetyError('invalid-limit', `ZIP import: ${detail}.`);\n}\n\nfunction originalEntryPath(entry: JSZip.JSZipObject, relativePath: string): string {\n return (\n (\n entry as JSZip.JSZipObject & {\n unsafeOriginalName?: string;\n }\n ).unsafeOriginalName ?? relativePath\n );\n}\n\nfunction compressedMetadata(\n entry: JSZip.JSZipObject,\n path: string,\n): { uncompressedSize?: number; compressedSize?: number; crc32?: number } {\n const data = (\n entry as unknown as {\n _data?: { uncompressedSize?: number; compressedSize?: number; crc32?: number };\n }\n )._data;\n const uncompressedSize = data?.uncompressedSize;\n const compressedSize = data?.compressedSize;\n const crc32 = data?.crc32;\n if (\n (uncompressedSize !== undefined && !isValidMetadataInteger(uncompressedSize)) ||\n (compressedSize !== undefined && !isValidMetadataInteger(compressedSize)) ||\n (crc32 !== undefined && !Number.isInteger(crc32))\n ) {\n throw invalidMetadataError(path);\n }\n return {\n ...(uncompressedSize !== undefined ? { uncompressedSize } : {}),\n ...(compressedSize !== undefined ? { compressedSize } : {}),\n ...(crc32 !== undefined ? { crc32 } : {}),\n };\n}\n\nfunction isValidMetadataInteger(value: number): boolean {\n return Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction invalidMetadataError(path: string): ZipSafetyError {\n return new ZipSafetyError(\n 'invalid-entry-metadata',\n `ZIP import: archive member has invalid size or checksum metadata: ${path}.`,\n { path },\n );\n}\n\nfunction isSafeArchivePath(path: string): boolean {\n const segments = path.split('/');\n return !(\n path.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(path) ||\n path.includes('\\\\') ||\n Array.from(path).some((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint < 0x20 || codePoint === 0x7f;\n }) ||\n segments.some((segment) => segment === '' || segment === '.' || segment === '..')\n );\n}\n","/** Incremental JSZip member reads governed by one shared archive budget. */\n\nimport JSZip from 'jszip';\nimport {\n archiveTooLargeError,\n assertNonNegativeFinite,\n compressionRatio,\n compressionRatioError,\n entryTooLargeError,\n resolveZipSafetyLimits,\n throwIfZipAborted,\n validateZipArchive,\n tooManyEntriesError,\n ZipSafetyError,\n type ResolvedZipSafetyLimits,\n type ValidatedZipEntry,\n type ZipSafetyErrorCode,\n type ZipSafetyLimits,\n} from './zipLimits.js';\nimport { declaredZipEntryCount, prepareZipInput, type ZipInput } from './zipEntryCount.js';\n\ninterface ActiveZipRead {\n stop(error: unknown): void;\n}\n\ninterface ZipChunkStream {\n on(event: 'data', callback: (chunk: Uint8Array) => void): ZipChunkStream;\n on(event: 'error', callback: (error: unknown) => void): ZipChunkStream;\n on(event: 'end', callback: () => void): ZipChunkStream;\n pause(): ZipChunkStream;\n resume(): ZipChunkStream;\n}\n\ninterface StreamableZipObject {\n internalStream(type: 'uint8array'): ZipChunkStream;\n}\n\n/**\n * A JSZip archive whose member reads share one actual-byte budget.\n *\n * JSZip's promise API accumulates a complete inflated member before returning.\n * This wrapper instead consumes `internalStream('uint8array')`, checks every\n * emitted chunk, and pauses every active stream as soon as any limit fails.\n * Completed reads are cached so repeated OOXML part access is neither inflated\n * nor charged twice.\n */\nexport interface BoundedZipArchive {\n readonly zip: JSZip;\n readonly entries: readonly ValidatedZipEntry[];\n readonly emittedUncompressedBytes: number;\n read(path: string, maxBytes?: number): Promise<Uint8Array | null>;\n readText(path: string, maxBytes?: number): Promise<string | null>;\n release(path: string): void;\n}\n\nclass JsZipBoundedArchive implements BoundedZipArchive {\n readonly zip: JSZip;\n readonly entries: readonly ValidatedZipEntry[];\n\n private readonly limits: ResolvedZipSafetyLimits;\n private readonly entriesByPath: ReadonlyMap<string, ValidatedZipEntry>;\n private readonly signal?: AbortSignal;\n private readonly reads = new Map<string, Promise<Uint8Array>>();\n private readonly activeReads = new Set<ActiveZipRead>();\n private terminalError?: ZipSafetyError;\n private emittedBytes = 0;\n\n constructor(\n zip: JSZip,\n entries: readonly ValidatedZipEntry[],\n limits: ResolvedZipSafetyLimits,\n signal?: AbortSignal,\n ) {\n this.zip = zip;\n this.entries = entries;\n this.limits = limits;\n this.signal = signal;\n this.entriesByPath = new Map(entries.map((entry) => [entry.path, entry]));\n }\n\n /** Actual uncompressed bytes accepted from JSZip streams so far. */\n get emittedUncompressedBytes(): number {\n return this.emittedBytes;\n }\n\n /** Read and cache one member, returning null when it does not exist. */\n async read(path: string, maxBytes?: number): Promise<Uint8Array | null> {\n throwIfZipAborted(this.signal);\n this.throwIfFailed();\n if (maxBytes !== undefined) assertNonNegativeFinite('member read limit', maxBytes);\n const entry = this.entriesByPath.get(path);\n if (!entry) return null;\n\n let pending = this.reads.get(path);\n if (!pending) {\n pending = this.streamEntry(entry, maxBytes);\n this.reads.set(path, pending);\n }\n let bytes: Uint8Array;\n try {\n bytes = await pending;\n } catch (error: unknown) {\n // Cancellation is scoped to the current operation, not archive\n // corruption. Do not leave a cancelled read cached for a later caller.\n if (this.reads.get(path) === pending && !this.terminalError) this.reads.delete(path);\n throw error;\n }\n throwIfZipAborted(this.signal);\n if (maxBytes !== undefined && bytes.byteLength > maxBytes) {\n const error = entryTooLargeError(path, bytes.byteLength, maxBytes);\n this.failArchive(error);\n throw error;\n }\n return bytes;\n }\n\n /** Read one UTF-8 text member, returning null when it does not exist. */\n async readText(path: string, maxBytes?: number): Promise<string | null> {\n const bytes = await this.read(path, maxBytes);\n return bytes ? new TextDecoder().decode(bytes) : null;\n }\n\n /** Release a cached member when a sequential consumer has taken ownership. */\n release(path: string): void {\n this.reads.delete(path);\n }\n\n private streamEntry(metadata: ValidatedZipEntry, readLimit?: number): Promise<Uint8Array> {\n throwIfZipAborted(this.signal);\n const { path, entry, declaredSize, crc32: expectedCrc } = metadata;\n const effectiveEntryLimit = Math.min(\n this.limits.maxEntryUncompressedBytes,\n readLimit ?? Number.POSITIVE_INFINITY,\n );\n // Never trust a declaration enough to allocate it up front. A forged\n // 511 MiB size must not force 511 MiB of memory for a tiny member.\n let output: Uint8Array = new Uint8Array(0);\n let stream: ZipChunkStream;\n try {\n stream = (entry as unknown as StreamableZipObject).internalStream('uint8array');\n } catch (error: unknown) {\n const safeError = asZipSafetyError(error, path);\n this.failArchive(safeError);\n return Promise.reject(safeError);\n }\n\n return new Promise<Uint8Array>((resolve, reject) => {\n let settled = false;\n let written = 0;\n let crc = 0xffffffff;\n const removeAbortListener = (): void =>\n this.signal?.removeEventListener('abort', handleAbort);\n const handleAbort = (): void =>\n controller.stop(this.signal?.reason ?? new Error('ZIP operation was cancelled'));\n\n const controller: ActiveZipRead = {\n stop: (error) => {\n if (settled) return;\n settled = true;\n this.activeReads.delete(controller);\n removeAbortListener();\n // Public JSZip cancellation is cooperative. It prevents another\n // compressed-input tick; data already emitted synchronously by the\n // current tick is ignored because `settled` is now true.\n try {\n stream.pause();\n } catch {\n // Reject with the original safety failure even if a malformed\n // third-party stream cannot be paused cleanly.\n }\n reject(error);\n },\n };\n this.activeReads.add(controller);\n if (this.signal) {\n this.signal.addEventListener('abort', handleAbort, { once: true });\n if (this.signal.aborted) {\n handleAbort();\n return;\n }\n }\n\n stream\n .on('data', (chunk: Uint8Array) => {\n if (settled) return;\n const nextWritten = written + chunk.byteLength;\n try {\n throwIfZipAborted(this.signal);\n this.assertChunkAllowed(metadata, nextWritten, chunk.byteLength, effectiveEntryLimit);\n output = growOutput(output, nextWritten, declaredSize, effectiveEntryLimit);\n output.set(chunk, written);\n written = nextWritten;\n crc = updateCrc32(crc, chunk);\n } catch (error: unknown) {\n if (this.signal?.aborted) controller.stop(error);\n else this.failArchive(asZipSafetyError(error, path));\n }\n })\n .on('error', (error: unknown) => {\n if (settled) return;\n const message = error instanceof Error ? error.message : String(error);\n const code: ZipSafetyErrorCode = message.includes('uncompressed data size mismatch')\n ? 'size-mismatch'\n : 'decompression-failed';\n this.failArchive(\n new ZipSafetyError(code, `ZIP import: could not decompress ${path}: ${message}`, {\n path,\n cause: error,\n }),\n );\n })\n .on('end', () => {\n if (settled) return;\n if (declaredSize !== undefined && written !== declaredSize) {\n this.failArchive(\n new ZipSafetyError(\n 'size-mismatch',\n `ZIP import: archive member size does not match its metadata: ${path}.`,\n { path, actual: written },\n ),\n );\n return;\n }\n const actualCrc = (crc ^ 0xffffffff) >>> 0;\n if (expectedCrc !== undefined && actualCrc !== expectedCrc >>> 0) {\n this.failArchive(\n new ZipSafetyError('crc-mismatch', `ZIP import: CRC32 mismatch for ${path}.`, {\n path,\n }),\n );\n return;\n }\n\n settled = true;\n this.activeReads.delete(controller);\n removeAbortListener();\n resolve(output.byteLength === written ? output : output.slice(0, written));\n })\n .resume();\n });\n }\n\n private assertChunkAllowed(\n entry: ValidatedZipEntry,\n nextEntryBytes: number,\n chunkBytes: number,\n effectiveEntryLimit: number,\n ): void {\n if (entry.declaredSize !== undefined && nextEntryBytes > entry.declaredSize) {\n throw new ZipSafetyError(\n 'size-mismatch',\n `ZIP import: archive member size does not match its metadata: ${entry.path}.`,\n { path: entry.path, actual: nextEntryBytes },\n );\n }\n if (\n effectiveEntryLimit < this.limits.maxUncompressedBytes &&\n nextEntryBytes > effectiveEntryLimit\n ) {\n throw entryTooLargeError(entry.path, nextEntryBytes, effectiveEntryLimit);\n }\n\n const nextTotal = this.emittedBytes + chunkBytes;\n if (nextTotal > this.limits.maxUncompressedBytes) {\n throw archiveTooLargeError(nextTotal, this.limits.maxUncompressedBytes);\n }\n if (nextEntryBytes > effectiveEntryLimit) {\n throw entryTooLargeError(entry.path, nextEntryBytes, effectiveEntryLimit);\n }\n\n const ratio =\n entry.compressedSize === undefined\n ? undefined\n : compressionRatio(nextEntryBytes, entry.compressedSize);\n if (ratio !== undefined && ratio > this.limits.maxCompressionRatio) {\n throw compressionRatioError(entry.path, ratio, this.limits.maxCompressionRatio);\n }\n this.emittedBytes = nextTotal;\n }\n\n private failArchive(error: ZipSafetyError): void {\n if (this.terminalError) return;\n this.terminalError = error;\n for (const active of [...this.activeReads]) active.stop(error);\n }\n\n private throwIfFailed(): void {\n if (this.terminalError) throw this.terminalError;\n }\n}\n\n/** Load with JSZip, validate metadata, and create a shared bounded read session. */\nexport async function openBoundedZipArchive(\n data: ZipInput,\n limits: ZipSafetyLimits = {},\n): Promise<BoundedZipArchive> {\n throwIfZipAborted(limits.signal);\n const resolved = resolveZipSafetyLimits(limits);\n let prepared: { input: ZipInput; bytes?: Uint8Array };\n try {\n prepared = await prepareZipInput(data, limits.signal);\n } catch (error: unknown) {\n if (limits.signal?.aborted) throw limits.signal.reason ?? error;\n throw invalidArchiveError(error);\n }\n throwIfZipAborted(limits.signal);\n if (prepared.bytes) {\n const declaredEntries = declaredZipEntryCount(prepared.bytes);\n if (declaredEntries !== undefined && declaredEntries > resolved.maxEntries) {\n throw tooManyEntriesError(declaredEntries, resolved.maxEntries);\n }\n }\n let zip: JSZip;\n try {\n zip = await JSZip.loadAsync(prepared.input);\n } catch (error: unknown) {\n if (limits.signal?.aborted) throw limits.signal.reason ?? error;\n throw invalidArchiveError(error);\n }\n throwIfZipAborted(limits.signal);\n return createBoundedZipArchive(zip, limits);\n}\n\nfunction invalidArchiveError(cause: unknown): ZipSafetyError {\n return new ZipSafetyError('invalid-archive', 'ZIP import: archive is not readable.', { cause });\n}\n\n/** Wrap an existing JSZip instance in the same validation and shared budget. */\nexport function createBoundedZipArchive(\n zip: JSZip,\n limits: ZipSafetyLimits = {},\n): BoundedZipArchive {\n throwIfZipAborted(limits.signal);\n const resolved = resolveZipSafetyLimits(limits);\n const entries = validateZipArchive(zip, limits);\n throwIfZipAborted(limits.signal);\n return new JsZipBoundedArchive(zip, entries, resolved, limits.signal);\n}\n\nfunction asZipSafetyError(error: unknown, path: string): ZipSafetyError {\n if (error instanceof ZipSafetyError) return error;\n return new ZipSafetyError(\n 'decompression-failed',\n `ZIP import: could not decompress ${path}: ${error instanceof Error ? error.message : String(error)}`,\n { path, cause: error },\n );\n}\n\nfunction growOutput(\n current: Uint8Array,\n required: number,\n declaredSize: number | undefined,\n limit: number,\n): Uint8Array {\n if (required <= current.byteLength) return current;\n const maximum = Math.min(declaredSize ?? limit, limit);\n const grownSize = current.byteLength === 0 ? required : current.byteLength * 2;\n const nextSize = Math.min(maximum, Math.max(required, grownSize));\n const next = new Uint8Array(nextSize);\n next.set(current);\n return next;\n}\n\nlet crcTable: Uint32Array | undefined;\n\nfunction updateCrc32(crc: number, bytes: Uint8Array): number {\n const table = crcTable ?? (crcTable = makeCrcTable());\n let next = crc >>> 0;\n for (const byte of bytes) next = table[(next ^ byte) & 0xff]! ^ (next >>> 8);\n return next >>> 0;\n}\n\nfunction makeCrcTable(): Uint32Array {\n const table = new Uint32Array(256);\n for (let n = 0; n < table.length; n++) {\n let value = n;\n for (let bit = 0; bit < 8; bit++) {\n value = (value & 1) !== 0 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;\n }\n table[n] = value >>> 0;\n }\n return table;\n}\n","/** Narrow EOCD preflight for rejecting record-count bombs before JSZip allocation. */\n\nimport { throwIfZipAborted } from './zipLimits.js';\n\nconst END_OF_CENTRAL_DIRECTORY = 0x06054b50;\nconst ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50;\nconst ZIP64_END_LOCATOR = 0x07064b50;\nconst UINT16_MAX = 0xffff;\nconst MIN_EOCD_BYTES = 22;\nconst MAX_ZIP_COMMENT_BYTES = 0xffff;\n\nexport type ZipInput = ArrayBuffer | Uint8Array | Blob;\n\n/** Materialize Blob input once when possible so preflight bytes feed JSZip too. */\nexport async function prepareZipInput(\n data: ZipInput,\n signal?: AbortSignal,\n): Promise<{ input: ZipInput; bytes?: Uint8Array }> {\n throwIfZipAborted(signal);\n if (ArrayBuffer.isView(data)) {\n const bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n return { input: bytes, bytes };\n }\n if (!isBlobLike(data)) {\n const bytes = new Uint8Array(data);\n return { input: bytes, bytes };\n }\n\n const bytes = await readBlobBytes(data, signal);\n throwIfZipAborted(signal);\n return bytes ? { input: bytes, bytes } : { input: data };\n}\n\n/**\n * Read the declared total central-directory record count when unambiguous.\n * Returns undefined for absent, trailing, malformed, or unsupported metadata;\n * JSZip remains authoritative in those cases.\n */\nexport function declaredZipEntryCount(bytes: Uint8Array): number | undefined {\n const eocdOffset = findEocd(bytes);\n if (eocdOffset === undefined) return undefined;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const count = view.getUint16(eocdOffset + 10, true);\n if (count !== UINT16_MAX) return count;\n return readZip64EntryCount(view, eocdOffset);\n}\n\nfunction findEocd(bytes: Uint8Array): number | undefined {\n if (bytes.byteLength < MIN_EOCD_BYTES) return undefined;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const earliest = Math.max(0, bytes.byteLength - MIN_EOCD_BYTES - MAX_ZIP_COMMENT_BYTES);\n for (let offset = bytes.byteLength - MIN_EOCD_BYTES; offset >= earliest; offset--) {\n if (view.getUint32(offset, true) !== END_OF_CENTRAL_DIRECTORY) continue;\n const commentLength = view.getUint16(offset + 20, true);\n if (offset + MIN_EOCD_BYTES + commentLength === bytes.byteLength) return offset;\n }\n return undefined;\n}\n\nfunction readZip64EntryCount(view: DataView, eocdOffset: number): number | undefined {\n const locatorOffset = eocdOffset - 20;\n if (locatorOffset < 0 || view.getUint32(locatorOffset, true) !== ZIP64_END_LOCATOR) {\n return undefined;\n }\n const recordOffset = readSafeUint64(view, locatorOffset + 8);\n if (\n recordOffset === undefined ||\n recordOffset < 0 ||\n recordOffset + 40 > view.byteLength ||\n view.getUint32(recordOffset, true) !== ZIP64_END_OF_CENTRAL_DIRECTORY\n ) {\n return undefined;\n }\n return readSafeUint64(view, recordOffset + 32);\n}\n\nfunction readSafeUint64(view: DataView, offset: number): number | undefined {\n if (offset < 0 || offset + 8 > view.byteLength) return undefined;\n const low = view.getUint32(offset, true);\n const high = view.getUint32(offset + 4, true);\n const value = high * 0x1_0000_0000 + low;\n return Number.isSafeInteger(value) ? value : undefined;\n}\n\nfunction isBlobLike(data: ZipInput): data is Blob {\n const candidate = data as Blob;\n return typeof candidate.size === 'number' && typeof candidate.slice === 'function';\n}\n\nasync function readBlobBytes(blob: Blob, signal?: AbortSignal): Promise<Uint8Array | undefined> {\n const arrayBuffer = (blob as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }).arrayBuffer;\n if (typeof arrayBuffer === 'function') {\n return new Uint8Array(await raceWithAbort(arrayBuffer.call(blob), signal));\n }\n if (typeof FileReader === 'undefined') return undefined;\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n let settled = false;\n const cleanup = (): void => signal?.removeEventListener('abort', handleAbort);\n const finish = (work: () => void): void => {\n if (settled) return;\n settled = true;\n cleanup();\n work();\n };\n const handleAbort = (): void => {\n if (settled) return;\n const reason = signal?.reason ?? new Error('ZIP operation was cancelled');\n // Mark the promise settled before FileReader.abort(): browsers are\n // allowed to dispatch `abort` synchronously and re-enter this handler.\n finish(() => reject(reason));\n try {\n reader.abort();\n } catch {\n // Preserve the caller's reason if a platform reader is already done.\n }\n };\n reader.onload = () => finish(() => resolve(new Uint8Array(reader.result as ArrayBuffer)));\n reader.onerror = () =>\n finish(() => reject(reader.error ?? new Error('Could not read ZIP blob.')));\n reader.onabort = handleAbort;\n signal?.addEventListener('abort', handleAbort, { once: true });\n if (signal?.aborted) {\n handleAbort();\n return;\n }\n reader.readAsArrayBuffer(blob);\n });\n}\n\nfunction raceWithAbort<T>(work: Promise<T>, signal?: AbortSignal): Promise<T> {\n throwIfZipAborted(signal);\n if (!signal) return work;\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const finish = (complete: () => void): void => {\n if (settled) return;\n settled = true;\n signal.removeEventListener('abort', handleAbort);\n complete();\n };\n const handleAbort = (): void =>\n finish(() => reject(signal.reason ?? new Error('ZIP operation was cancelled')));\n signal.addEventListener('abort', handleAbort, { once: true });\n if (signal.aborted) handleAbort();\n work.then(\n (value) => finish(() => resolve(value)),\n (error: unknown) => finish(() => reject(error)),\n );\n });\n}\n"],"mappings":"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","names":["bytes"]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
ZipSafetyError,
|
|
6
6
|
openBoundedZipArchive
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FE6OJV6O.js";
|
|
8
8
|
|
|
9
9
|
// src/registry/types.ts
|
|
10
10
|
var BUILTIN_FORMAT_IDS = [
|
|
@@ -435,9 +435,12 @@ function defaultFormats() {
|
|
|
435
435
|
const { zipToContainer } = await import("./container/index.js");
|
|
436
436
|
return zipToContainer(data, optionsFor(options, "dbk"));
|
|
437
437
|
},
|
|
438
|
-
async exportDoc(input) {
|
|
438
|
+
async exportDoc(input, options) {
|
|
439
439
|
const { containerToZip } = await import("./container/index.js");
|
|
440
|
-
const blob = await containerToZip(
|
|
440
|
+
const blob = await containerToZip(
|
|
441
|
+
await snapshotContainerWithDocument(input),
|
|
442
|
+
optionsFor(options, "dbk")
|
|
443
|
+
);
|
|
441
444
|
return ok(await toBytes(blob), MIME.zip);
|
|
442
445
|
}
|
|
443
446
|
};
|
|
@@ -482,6 +485,13 @@ function defaultRegistry() {
|
|
|
482
485
|
function markdownFormatOptions(options) {
|
|
483
486
|
return options.formatOptions?.md ?? {};
|
|
484
487
|
}
|
|
488
|
+
function throwIfAborted(signal) {
|
|
489
|
+
if (!signal?.aborted) return;
|
|
490
|
+
if (signal.reason !== void 0) throw signal.reason;
|
|
491
|
+
const error = new Error("Document conversion was cancelled.");
|
|
492
|
+
error.name = "AbortError";
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
485
495
|
function hasPrefix(bytes, prefix) {
|
|
486
496
|
if (bytes.length < prefix.length) return false;
|
|
487
497
|
for (let i = 0; i < prefix.length; i++) {
|
|
@@ -640,23 +650,18 @@ async function applyTransformStyle(input, transformStyle, themeId, registry) {
|
|
|
640
650
|
}
|
|
641
651
|
return [];
|
|
642
652
|
}
|
|
643
|
-
async function
|
|
653
|
+
async function prepareConversion(source, options = {}) {
|
|
654
|
+
return prepareConversionInternal(source, options);
|
|
655
|
+
}
|
|
656
|
+
async function prepareConversionInternal(source, options, failureFormat) {
|
|
657
|
+
throwIfAborted(options.signal);
|
|
644
658
|
const registry = options.registry ?? defaultRegistry();
|
|
645
|
-
const target = registry.get(to);
|
|
646
|
-
if (!target) {
|
|
647
|
-
throw new ConversionError("unknown-format", `Unknown target format "${to}".`, { format: to });
|
|
648
|
-
}
|
|
649
|
-
if (!target.exportDoc) {
|
|
650
|
-
throw new ConversionError(
|
|
651
|
-
"unsupported-output",
|
|
652
|
-
`Format "${target.label}" does not support export.`,
|
|
653
|
-
{ format: to, hint: "This format is import-only." }
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
659
|
let normalized;
|
|
657
660
|
try {
|
|
658
661
|
normalized = await normalize(source, options, registry);
|
|
662
|
+
throwIfAborted(options.signal);
|
|
659
663
|
} catch (err) {
|
|
664
|
+
throwIfAborted(options.signal);
|
|
660
665
|
if (err instanceof ConversionError) throw err;
|
|
661
666
|
const message = err instanceof Error ? err.message : String(err);
|
|
662
667
|
throw new ConversionError("invalid-input", message, {
|
|
@@ -668,7 +673,6 @@ async function convert(source, to, options = {}) {
|
|
|
668
673
|
if (options.themeId && !input.doc.themeId) {
|
|
669
674
|
input.doc = { ...input.doc, themeId: options.themeId };
|
|
670
675
|
}
|
|
671
|
-
let result;
|
|
672
676
|
try {
|
|
673
677
|
if (options.transformStyle) {
|
|
674
678
|
warnings.push(
|
|
@@ -679,26 +683,85 @@ async function convert(source, to, options = {}) {
|
|
|
679
683
|
options.transformRegistry
|
|
680
684
|
)
|
|
681
685
|
);
|
|
686
|
+
throwIfAborted(options.signal);
|
|
682
687
|
}
|
|
683
|
-
result = await target.exportDoc(input, options);
|
|
684
688
|
} catch (err) {
|
|
689
|
+
throwIfAborted(options.signal);
|
|
685
690
|
if (err instanceof ConversionError) throw err;
|
|
686
691
|
const message = err instanceof Error ? err.message : String(err);
|
|
687
|
-
throw new ConversionError("conversion-failed", message, {
|
|
692
|
+
throw new ConversionError("conversion-failed", message, {
|
|
693
|
+
format: failureFormat,
|
|
694
|
+
cause: err
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
const preparedWarnings = Object.freeze([...warnings]);
|
|
698
|
+
const preparedInput = { ...input };
|
|
699
|
+
return Object.freeze({
|
|
700
|
+
async convert(to, targetOptions = {}) {
|
|
701
|
+
const target = requireExportTarget(registry, to);
|
|
702
|
+
const exportOptions = mergePreparedExportOptions(options, targetOptions);
|
|
703
|
+
throwIfAborted(exportOptions.signal);
|
|
704
|
+
let result;
|
|
705
|
+
try {
|
|
706
|
+
result = await target.exportDoc({ ...preparedInput }, exportOptions);
|
|
707
|
+
throwIfAborted(exportOptions.signal);
|
|
708
|
+
} catch (err) {
|
|
709
|
+
throwIfAborted(exportOptions.signal);
|
|
710
|
+
if (err instanceof ConversionError) throw err;
|
|
711
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
712
|
+
throw new ConversionError("conversion-failed", message, { format: to, cause: err });
|
|
713
|
+
}
|
|
714
|
+
const ext = target.extensions[0]?.replace(/^\.+/, "") ?? to;
|
|
715
|
+
return {
|
|
716
|
+
...result,
|
|
717
|
+
suggestedFilename: `${preparedInput.baseName}.${ext}`,
|
|
718
|
+
warnings: [...preparedWarnings, ...result.warnings]
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
function requireExportTarget(registry, to) {
|
|
724
|
+
const target = registry.get(to);
|
|
725
|
+
if (!target) {
|
|
726
|
+
throw new ConversionError("unknown-format", `Unknown target format "${to}".`, { format: to });
|
|
688
727
|
}
|
|
689
|
-
|
|
728
|
+
if (!target.exportDoc) {
|
|
729
|
+
throw new ConversionError(
|
|
730
|
+
"unsupported-output",
|
|
731
|
+
`Format "${target.label}" does not support export.`,
|
|
732
|
+
{ format: to, hint: "This format is import-only." }
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
return target;
|
|
736
|
+
}
|
|
737
|
+
function mergePreparedExportOptions(prepared, target) {
|
|
690
738
|
return {
|
|
691
|
-
...
|
|
692
|
-
|
|
693
|
-
|
|
739
|
+
...prepared,
|
|
740
|
+
signal: mergeAbortSignals(prepared.signal, target.signal),
|
|
741
|
+
title: target.title ?? prepared.title,
|
|
742
|
+
resolvePlayerScript: target.resolvePlayerScript ?? prepared.resolvePlayerScript,
|
|
743
|
+
formatOptions: target.formatOptions === void 0 ? prepared.formatOptions : { ...prepared.formatOptions ?? {}, ...target.formatOptions }
|
|
694
744
|
};
|
|
695
745
|
}
|
|
746
|
+
function mergeAbortSignals(preparedSignal, targetSignal) {
|
|
747
|
+
if (!preparedSignal) return targetSignal;
|
|
748
|
+
if (!targetSignal || preparedSignal === targetSignal) return preparedSignal;
|
|
749
|
+
return AbortSignal.any([preparedSignal, targetSignal]);
|
|
750
|
+
}
|
|
751
|
+
async function convert(source, to, options = {}) {
|
|
752
|
+
throwIfAborted(options.signal);
|
|
753
|
+
const registry = options.registry ?? defaultRegistry();
|
|
754
|
+
requireExportTarget(registry, to);
|
|
755
|
+
const prepared = await prepareConversionInternal(source, { ...options, registry }, to);
|
|
756
|
+
return prepared.convert(to);
|
|
757
|
+
}
|
|
696
758
|
|
|
697
759
|
export {
|
|
698
760
|
BUILTIN_FORMAT_IDS,
|
|
699
761
|
defaultFormats,
|
|
700
762
|
createRegistry,
|
|
701
763
|
defaultRegistry,
|
|
764
|
+
prepareConversion,
|
|
702
765
|
convert
|
|
703
766
|
};
|
|
704
|
-
//# sourceMappingURL=chunk-
|
|
767
|
+
//# sourceMappingURL=chunk-HTW2M27H.js.map
|